├── system ├── env.zsh ├── keys.zsh ├── _path.zsh ├── grc.zsh └── aliases.zsh ├── git ├── gitignore.symlink ├── gitconfig.local.symlink.example ├── completion.zsh ├── aliases.zsh └── gitconfig.symlink ├── homebrew ├── path.zsh └── install.sh ├── .gitignore ├── bin ├── git-all ├── git-amend ├── git-undo ├── ee ├── set-defaults ├── git-unpushed ├── dns-flush ├── headers ├── git-copy-branch-name ├── git-track ├── git-nuke ├── yt ├── search ├── git-delete-local-merged ├── git-credit ├── git-unpushed-stat ├── e ├── git-edit-new ├── gitio ├── todo ├── git-promote ├── battery-status ├── git-up ├── dot ├── git-rank-contributors └── git-wtf ├── docker └── aliases.zsh ├── functions ├── gf ├── _c ├── _git-rm ├── c ├── _boom ├── extract └── _brew ├── ruby ├── aliases.zsh ├── rbenv.zsh ├── gemrc.symlink ├── completion.zsh └── irbrc.symlink ├── zsh ├── aliases.zsh ├── fpath.zsh ├── completion.zsh ├── window.zsh ├── config.zsh ├── zshrc.symlink └── prompt.zsh ├── vim └── vimrc.symlink ├── yarn └── path.zsh ├── xcode └── aliases.zsh ├── script ├── install └── bootstrap ├── Brewfile ├── LICENSE.md ├── macos ├── set-hostname.sh └── set-defaults.sh └── README.md /system/env.zsh: -------------------------------------------------------------------------------- 1 | export EDITOR='code' 2 | -------------------------------------------------------------------------------- /git/gitignore.symlink: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *.swp 4 | -------------------------------------------------------------------------------- /homebrew/path.zsh: -------------------------------------------------------------------------------- 1 | export PATH="/opt/homebrew/bin:$PATH" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | git/gitconfig.local.symlink 2 | Brewfile.lock.json 3 | -------------------------------------------------------------------------------- /bin/git-all: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Stage all unstaged. 4 | 5 | git add -A 6 | -------------------------------------------------------------------------------- /docker/aliases.zsh: -------------------------------------------------------------------------------- 1 | alias d='docker $*' 2 | alias d-c='docker-compose $*' 3 | -------------------------------------------------------------------------------- /functions/gf: -------------------------------------------------------------------------------- 1 | gf() { 2 | local branch=$1 3 | git checkout -b $branch origin/$branch 4 | } -------------------------------------------------------------------------------- /ruby/aliases.zsh: -------------------------------------------------------------------------------- 1 | alias sc='script/console' 2 | alias sg='script/generate' 3 | alias sd='script/destroy' 4 | -------------------------------------------------------------------------------- /zsh/aliases.zsh: -------------------------------------------------------------------------------- 1 | alias reload!='. ~/.zshrc' 2 | 3 | alias cls='clear' # Good 'ol Clear Screen command 4 | -------------------------------------------------------------------------------- /ruby/rbenv.zsh: -------------------------------------------------------------------------------- 1 | # init according to man page 2 | if (( $+commands[rbenv] )) 3 | then 4 | eval "$(rbenv init -)" 5 | fi 6 | -------------------------------------------------------------------------------- /bin/git-amend: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Use the last commit message and amend your stuffs. 4 | 5 | git commit --amend -C HEAD 6 | -------------------------------------------------------------------------------- /bin/git-undo: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Undo your last commit, but don't throw away your changes 4 | 5 | git reset --soft HEAD^ 6 | -------------------------------------------------------------------------------- /vim/vimrc.symlink: -------------------------------------------------------------------------------- 1 | syntax on 2 | 3 | " Wrap gitcommit file types at the appropriate length 4 | filetype indent plugin on 5 | -------------------------------------------------------------------------------- /bin/ee: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # vscode requires `--wait` if you're editing interactively in a prompt. 4 | # 5 | EDITOR="$EDITOR --wait" $@ 6 | -------------------------------------------------------------------------------- /bin/set-defaults: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Sets macOS defaults by running $ZSH/macos/set-defaults.sh. 4 | 5 | exec $ZSH/macos/set-defaults.sh 6 | -------------------------------------------------------------------------------- /ruby/gemrc.symlink: -------------------------------------------------------------------------------- 1 | --- 2 | :update_sources: true 3 | :verbose: true 4 | :backtrace: false 5 | :benchmark: false 6 | gem: --no-document 7 | -------------------------------------------------------------------------------- /functions/_c: -------------------------------------------------------------------------------- 1 | #compdef c 2 | _files -W $PROJECTS -/ 3 | _files -W $GOPATH/src/github.com -/ 4 | 5 | # See further documentation in `functions/c`. 6 | -------------------------------------------------------------------------------- /system/keys.zsh: -------------------------------------------------------------------------------- 1 | # Pipe my public key to my clipboard. 2 | alias pubkey="more ~/.ssh/id_rsa.pub | pbcopy | echo '=> Public key copied to pasteboard.'" 3 | -------------------------------------------------------------------------------- /yarn/path.zsh: -------------------------------------------------------------------------------- 1 | # sup yarn 2 | # https://yarnpkg.com 3 | 4 | if (( $+commands[yarn] )) 5 | then 6 | export PATH="$PATH:`yarn global bin`" 7 | fi 8 | -------------------------------------------------------------------------------- /git/gitconfig.local.symlink.example: -------------------------------------------------------------------------------- 1 | [user] 2 | name = AUTHORNAME 3 | email = AUTHOREMAIL 4 | [credential] 5 | helper = GIT_CREDENTIAL_HELPER -------------------------------------------------------------------------------- /system/_path.zsh: -------------------------------------------------------------------------------- 1 | export PATH="./bin:/usr/local/bin:/usr/local/sbin:$ZSH/bin:$PATH" 2 | export MANPATH="/usr/local/man:/usr/local/mysql/man:/usr/local/git/man:$MANPATH" 3 | -------------------------------------------------------------------------------- /bin/git-unpushed: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Show the diff of everything you haven't pushed yet. 4 | 5 | branch=$(git rev-parse --abbrev-ref HEAD) 6 | git diff origin/$branch..HEAD 7 | -------------------------------------------------------------------------------- /system/grc.zsh: -------------------------------------------------------------------------------- 1 | # GRC colorizes nifty unix tools all over the place 2 | if (( $+commands[grc] )) && (( $+commands[brew] )) 3 | then 4 | source `brew --prefix`/etc/grc.bashrc 5 | fi 6 | -------------------------------------------------------------------------------- /zsh/fpath.zsh: -------------------------------------------------------------------------------- 1 | #add each topic folder to fpath so that they can add functions and completion scripts 2 | for topic_folder ($ZSH/*) if [ -d $topic_folder ]; then fpath=($topic_folder $fpath); fi; 3 | -------------------------------------------------------------------------------- /bin/dns-flush: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # I literally always forget how to flush my DNS settings on macOS (in large part 4 | # because it changes every damn update). 5 | 6 | sudo killall -HUP mDNSResponder 7 | -------------------------------------------------------------------------------- /bin/headers: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # https://github.com/rtomayko/dotfiles/blob/rtomayko/.local/bin/headers 4 | 5 | curl -sv "$@" 2>&1 >/dev/null | 6 | grep -v "^\*" | 7 | grep -v "^}" | 8 | cut -c3- 9 | -------------------------------------------------------------------------------- /bin/git-copy-branch-name: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copy the current branch name to the clipboard. 4 | 5 | branch=$(git rev-parse --abbrev-ref HEAD) 6 | echo $branch 7 | echo $branch | tr -d '\n' | tr -d ' ' | pbcopy 8 | -------------------------------------------------------------------------------- /xcode/aliases.zsh: -------------------------------------------------------------------------------- 1 | alias ios="open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app" 2 | alias watchos="open /Applications/Xcode.app/Contents/Developer/Applications/Simulator\ \(Watch\).app" 3 | -------------------------------------------------------------------------------- /zsh/completion.zsh: -------------------------------------------------------------------------------- 1 | # matches case insensitive for lowercase 2 | zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' 3 | 4 | # pasting with tabs doesn't perform completion 5 | zstyle ':completion:*' insert-tab pending 6 | -------------------------------------------------------------------------------- /bin/git-track: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Sets up your branch to track a remote branch. Assumes you mean 4 | # `origin/$branch-name`. 5 | 6 | branch=$(git rev-parse --abbrev-ref HEAD) 7 | git branch $branch --set-upstream-to origin/$branch -------------------------------------------------------------------------------- /bin/git-nuke: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Nukes a branch locally and on the origin remote. 4 | # 5 | # $1 - Branch name. 6 | # 7 | # Examples 8 | # 9 | # git nuke add-git-nuke 10 | 11 | git branch -D $1 12 | git push origin :$1 13 | -------------------------------------------------------------------------------- /bin/yt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Downloads the embedded video on any web page straight to the desktop. 4 | # 5 | # youtube-dlp, which is awesome: 6 | # https://github.com/yt-dlp/yt-dlp 7 | # 8 | 9 | cd ~/Desktop && yt-dlp -S "ext" "$1" 10 | -------------------------------------------------------------------------------- /bin/search: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Quick search in a directory for a string ($@). 4 | # 5 | set -e 6 | 7 | # use -iru to search directories ack usually ignores (like .git) 8 | if [ -x /usr/bin/ack-grep ]; then 9 | ack-grep -i "$@" 10 | else 11 | ack -i "$@" 12 | fi 13 | -------------------------------------------------------------------------------- /bin/git-delete-local-merged: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Delete all local branches that have been merged into HEAD. Stolen from 4 | # our favorite @tekkub: 5 | # 6 | # https://plus.google.com/115587336092124934674/posts/dXsagsvLakJ 7 | 8 | git branch -d `git branch --merged | grep -v '^*' | grep -v 'master' | tr -d '\n'` 9 | -------------------------------------------------------------------------------- /script/install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Run all dotfiles installers. 4 | 5 | set -e 6 | 7 | cd "$(dirname $0)"/.. 8 | 9 | echo "› brew bundle" 10 | brew bundle 11 | 12 | # find the installers and run them iteratively 13 | find . -name install.sh | while read installer ; do sh -c "${installer}" ; done 14 | -------------------------------------------------------------------------------- /system/aliases.zsh: -------------------------------------------------------------------------------- 1 | # grc overides for ls 2 | # Made possible through contributions from generous benefactors like 3 | # `brew install coreutils` 4 | if $(gls &>/dev/null) 5 | then 6 | alias ls="gls -F --color" 7 | alias l="gls -lAh --color" 8 | alias ll="gls -l --color" 9 | alias la='gls -A --color' 10 | fi -------------------------------------------------------------------------------- /functions/_git-rm: -------------------------------------------------------------------------------- 1 | #compdef git-rm 2 | _arguments -S -A '-*' \ 3 | '-f[override the up-to-date check]' \ 4 | "-n[don't actually remove the files, just show if they exist in the index]" \ 5 | '-r[allow recursive removal when a leading directory-name is given]' \ 6 | '--cached[only remove files from the index]' && ret=0 7 | _files 8 | -------------------------------------------------------------------------------- /bin/git-credit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # A very slightly quicker way to credit an author on the latest commit. 4 | # 5 | # $1 - The full name of the author. 6 | # $2 - The email address of the author. 7 | # 8 | # Examples 9 | # 10 | # git credit "Zach Holman" zach@example.com 11 | # 12 | 13 | git commit --amend --author "$1 <$2>" -C HEAD 14 | -------------------------------------------------------------------------------- /git/completion.zsh: -------------------------------------------------------------------------------- 1 | # Uses git's autocompletion for inner commands. Assumes an install of git's 2 | # bash `git-completion` script at $completion below (this is where Homebrew 3 | # tosses it, at least). 4 | completion='$(brew --prefix)/share/zsh/site-functions/_git' 5 | 6 | if test -f $completion 7 | then 8 | source $completion 9 | fi 10 | -------------------------------------------------------------------------------- /bin/git-unpushed-stat: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Show the diffstat of everything you haven't pushed yet. 4 | 5 | branch=$(git rev-parse --abbrev-ref HEAD) 6 | count=$(git rev-list --count HEAD origin/$branch...HEAD) 7 | 8 | if [ "$count" -eq "1" ] 9 | then 10 | s='' 11 | else 12 | s='s' 13 | fi 14 | 15 | git diff --stat origin/$branch..HEAD 16 | echo " $count commit$s total" 17 | -------------------------------------------------------------------------------- /functions/c: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | # 3 | # This lets you quickly jump into a project directory. 4 | # 5 | # Type: 6 | # 7 | # c 8 | # 9 | # ...to autocomplete on all of your projects in the directories specified in 10 | # `functions/_c`. Typically I'm using it like: 11 | # 12 | # c holm/bo 13 | # 14 | # ...to quickly jump into holman/boom, for example. 15 | 16 | cd "$PROJECTS/$1" 17 | -------------------------------------------------------------------------------- /bin/e: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Quick shortcut to an editor. 4 | # 5 | # This means that as I travel back and forth between editors, hey, I don't have 6 | # to re-learn any arcane commands. Neat. 7 | # 8 | # USAGE: 9 | # 10 | # $ e 11 | # # => opens the current directory in your editor 12 | # 13 | # $ e . 14 | # $ e /usr/local 15 | # # => opens the specified directory in your editor 16 | exec "$EDITOR" "${1:-.}" 17 | -------------------------------------------------------------------------------- /bin/git-edit-new: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Open new, unstaged files in your $EDITOR. 4 | # 5 | # This is nice to have when you run a command line generator which generates a 6 | # file or three in your working directory, and you know you want to immediately 7 | # edit them in your editor next. Why waste time clicking around like some sort 8 | # of plebian when you can just run another command? 9 | 10 | $EDITOR $(git ls-files --others --exclude-standard) 11 | -------------------------------------------------------------------------------- /ruby/completion.zsh: -------------------------------------------------------------------------------- 1 | # Stolen from 2 | # https://github.com/sstephenson/rbenv/blob/master/completions/rbenv.zsh 3 | 4 | if [[ ! -o interactive ]]; then 5 | return 6 | fi 7 | 8 | compctl -K _rbenv rbenv 9 | 10 | _rbenv() { 11 | local word words completions 12 | read -cA words 13 | word="${words[2]}" 14 | 15 | if [ "${#words}" -eq 2 ]; then 16 | completions="$(rbenv commands)" 17 | else 18 | completions="$(rbenv completions "${word}")" 19 | fi 20 | 21 | reply=("${(ps:\n:)completions}") 22 | } -------------------------------------------------------------------------------- /zsh/window.zsh: -------------------------------------------------------------------------------- 1 | # From http://dotfiles.org/~_why/.zshrc 2 | # Sets the window title nicely no matter where you are 3 | function title() { 4 | # escape '%' chars in $1, make nonprintables visible 5 | a=${(V)1//\%/\%\%} 6 | 7 | # Truncate command, and join lines. 8 | a=$(print -Pn "%40>...>$a" | tr -d "\n") 9 | 10 | case $TERM in 11 | screen) 12 | print -Pn "\ek$a:$3\e\\" # screen title (in ^A") 13 | ;; 14 | xterm*|rxvt) 15 | print -Pn "\e]2;$2\a" # plain xterm title ($3 for pwd) 16 | ;; 17 | esac 18 | } 19 | 20 | -------------------------------------------------------------------------------- /homebrew/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Homebrew 4 | # 5 | # This installs some of the common dependencies needed (or at least desired) 6 | # using Homebrew. 7 | 8 | # Check for Homebrew 9 | if test ! $(which brew) 10 | then 11 | echo " Installing Homebrew for you." 12 | 13 | # Install the correct homebrew for each OS type 14 | if test "$(uname)" = "Darwin" 15 | then 16 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 17 | elif test "$(expr substr $(uname -s) 1 5)" = "Linux" 18 | then 19 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install)" 20 | fi 21 | 22 | fi 23 | 24 | exit 0 25 | -------------------------------------------------------------------------------- /bin/gitio: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: gitio URL [CODE] 3 | # 4 | # Turns a github.com URL 5 | # into a git.io URL 6 | # 7 | # Created by @defunkt: 8 | # https://gist.github.com/1209316 9 | # 10 | # Copies the git.io URL to your clipboard. 11 | 12 | url = ARGV[0] 13 | code = ARGV[1] 14 | 15 | if url !~ /^(https?:\/\/)?(gist\.)?github.com/ 16 | abort "* github.com URLs only" 17 | end 18 | 19 | if url !~ /^http/ 20 | url = "https://#{url}" 21 | end 22 | 23 | if code 24 | code = "-F code=#{code}" 25 | end 26 | 27 | output = `curl -i https://git.io -F 'url=#{url}' #{code} 2> /dev/null` 28 | if output =~ /Location: (.+)\n?/ 29 | puts $1 30 | `echo #$1 | pbcopy` 31 | else 32 | puts output 33 | end 34 | -------------------------------------------------------------------------------- /functions/_boom: -------------------------------------------------------------------------------- 1 | #compdef boom 2 | 3 | local state line cmds ret=1 4 | 5 | _arguments -C '1: :->cmds' '*: :->args' 6 | 7 | case $state in 8 | cmds) 9 | local -a cmds 10 | cmds=( 11 | 'all:show all items in all lists' 12 | 'edit:edit the boom JSON file in $EDITOR' 13 | 'help:help text' 14 | ) 15 | _describe -t commands 'boom command' cmds && ret=0 16 | _values 'lists' $(boom | awk '{print $1}') 17 | ;; 18 | args) 19 | case $line[1] in 20 | (boom|all|edit|help) 21 | ;; 22 | *) 23 | _values 'items' `boom $line[1] | awk '{print $1}' | sed -e 's/://'` 2>/dev/null && ret=0 24 | ;; 25 | esac 26 | ;; 27 | esac 28 | 29 | return ret 30 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | cask_args appdir: '/Applications' 2 | 3 | tap 'homebrew/bundle' 4 | 5 | brew 'cloudflared' 6 | brew 'imagemagick' 7 | brew 'postgresql@14' 8 | brew 'redis' 9 | brew 'ruby-build' 10 | brew 'spaceman-diff' 11 | brew 'spark' 12 | brew 'stripe/stripe-cli/stripe' 13 | brew 'stripe/stripe-mock/stripe-mock' 14 | brew 'wget' 15 | brew 'yt-dlp' 16 | 17 | cask '1password' 18 | cask 'airtable' 19 | cask 'android-platform-tools' 20 | cask 'arq' 21 | cask 'diffusionbee' 22 | cask 'handbrake' 23 | cask 'messenger' 24 | cask 'notion-calendar' 25 | cask 'plex' 26 | cask 'sonos' 27 | cask 'transmission' 28 | cask 'raycast' 29 | cask 'warp' 30 | cask 'whatsapp' 31 | cask 'visual-studio-code' 32 | cask 'vlc' 33 | cask 'zed' 34 | cask 'zoom' 35 | -------------------------------------------------------------------------------- /bin/todo: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Creates something for me to do. 4 | # 5 | # I've used literally every todo list, app, program, script, everything. Even 6 | # the ones you are building and haven't released yet. 7 | # 8 | # I've found that they're all nice in their nice ways, but I still don't use 9 | # them, thus defeating the purpose of a todo list. 10 | # 11 | # All `todo` does is put a file on my Desktop with the filename given. That's 12 | # it. I aggressively prune my desktop of old tasks and keep one or two on there 13 | # at a time. Once I've finished a todo, I just delete the file. That's it. 14 | # 15 | # Millions of dollars later and `touch` wins. 16 | 17 | # Run our new web 2.0 todo list application and raise millions of VC dollars. 18 | touch ~/Desktop/"$*" 19 | -------------------------------------------------------------------------------- /bin/git-promote: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Promotes a local topic branch to a remote tracking branch of the same name, 4 | # by pushing and then setting up the git config 5 | # 6 | # Thanks to ENTP: 7 | # http://hoth.entp.com/2008/11/10/improving-my-git-workflow 8 | 9 | curr_branch=$(git symbolic-ref -q HEAD | sed -e 's|^refs/heads/||') 10 | 11 | remote_branch=$(git branch -r | grep "origin/${curr_branch}") 12 | [ -z "${remote_branch}" ] && ( git push origin "${curr_branch}" ) 13 | 14 | origin=$(git config --get "branch.${curr_branch}.remote") 15 | [ -z "${origin}" ] && ( git config --add "branch.${curr_branch}.remote" origin ) 16 | 17 | merge=$(git config --get "branch.${curr_branch}.merge") 18 | [ -z "${merge}" ] && ( git config --add "branch.${curr_branch}.merge" "refs/heads/${curr_branch}" ) -------------------------------------------------------------------------------- /bin/battery-status: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # battery-status 4 | # 5 | # A quick little indicator for battery status on your Mac laptop, suitable for 6 | # display in your prompt. 7 | 8 | if test ! "$(uname)" = "Darwin" 9 | then 10 | printf "" 11 | exit 0 12 | fi 13 | 14 | battstat=$(pmset -g batt) 15 | time_left=$(echo $battstat | 16 | tail -1 | 17 | cut -f2 | 18 | awk -F"; " '{print $3}' | 19 | cut -d' ' -f1 20 | ) 21 | 22 | if [[ $(pmset -g ac) == *"No adapter attached."* ]] 23 | then 24 | emoji='🔋' 25 | else 26 | emoji='🔌' 27 | fi 28 | 29 | if [[ $time_left == *"(no"* || $time_left == *"not"* ]] 30 | then 31 | time_left='⌛️ ' 32 | fi 33 | 34 | if [[ $time_left == *"0:00"* ]] 35 | then 36 | time_left='⚡️ ' 37 | fi 38 | 39 | printf "\033[1;92m$emoji $time_left \033[0m" 40 | -------------------------------------------------------------------------------- /git/aliases.zsh: -------------------------------------------------------------------------------- 1 | # Use `hub` as our git wrapper: 2 | # http://defunkt.github.com/hub/ 3 | hub_path=$(which hub) 4 | if (( $+commands[hub] )) 5 | then 6 | alias git=$hub_path 7 | fi 8 | 9 | # The rest of my fun git aliases 10 | alias gl='git pull --prune' 11 | alias glog="git log --graph --pretty=format:'%Cred%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cgreen(%cr)%Creset' --abbrev-commit --date=relative" 12 | alias gp='git push origin HEAD' 13 | 14 | # Remove `+` and `-` from start of diff lines; just rely upon color. 15 | alias gd='git diff --color | sed "s/^\([^-+ ]*\)[-+ ]/\\1/" | less -r' 16 | 17 | alias gc='git commit' 18 | alias gca='git commit -a' 19 | alias gco='git checkout' 20 | alias gcb='git copy-branch-name' 21 | alias gb='git branch' 22 | alias gs='git status -sb' # upgrade your git if -sb breaks for you. it's fun. 23 | alias gac='git add -A && git commit -m' 24 | alias ge='git-edit-new' 25 | -------------------------------------------------------------------------------- /bin/git-up: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Usage: git-up 4 | # git-reup 5 | # 6 | # Like git-pull but show a short and sexy log of changes 7 | # immediately after merging (git-up) or rebasing (git-reup). 8 | # 9 | # Inspired by Kyle Neath's `git up' alias: 10 | # http://gist.github.com/249223 11 | # 12 | # Stolen from Ryan Tomayko 13 | # http://github.com/rtomayko/dotfiles/blob/rtomayko/bin/git-up 14 | 15 | set -e 16 | 17 | PULL_ARGS="$@" 18 | 19 | # when invoked as git-reup, run as `git pull --rebase' 20 | test "$(basename $0)" = "git-reup" && 21 | PULL_ARGS="--rebase $PULL_ARGS" 22 | 23 | git pull $PULL_ARGS 24 | 25 | # show diffstat of all changes if we're pulling with --rebase. not 26 | # sure why git-pull only does this when merging. 27 | test "$(basename $0)" = "git-reup" && { 28 | echo "Diff:" 29 | git --no-pager diff --color --stat HEAD@{1}.. | 30 | sed 's/^/ /' 31 | } 32 | 33 | # show an abbreviated commit log of stuff that was just merged. 34 | echo "Log:" 35 | git log --color --pretty=oneline --abbrev-commit HEAD@{1}.. | 36 | sed 's/^/ /' 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) Zach Holman, http://zachholman.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /git/gitconfig.symlink: -------------------------------------------------------------------------------- 1 | # Local/private config goes in the include 2 | [include] 3 | path = ~/.gitconfig.local 4 | [hub] 5 | protocol = https 6 | [alias] 7 | co = checkout 8 | promote = !$ZSH/bin/git-promote 9 | wtf = !$ZSH/bin/git-wtf 10 | rank-contributors = !$ZSH/bin/git-rank-contributors 11 | count = !git shortlog -sn 12 | [color] 13 | diff = auto 14 | status = auto 15 | branch = auto 16 | ui = true 17 | [core] 18 | excludesfile = ~/.gitignore 19 | editor = vim 20 | [apply] 21 | whitespace = nowarn 22 | [mergetool] 23 | keepBackup = false 24 | [difftool] 25 | prompt = false 26 | [help] 27 | autocorrect = 1 28 | [push] 29 | # See `git help config` (search for push.default) 30 | # for more information on different options of the below setting. 31 | # 32 | # Setting to git 2.0 default to suppress warning message 33 | default = simple 34 | [credential] 35 | helper = osxkeychain 36 | [diff "spaceman-diff"] 37 | command = /opt/homebrew/bin/spaceman-diff 38 | 39 | [init] 40 | defaultBranch = main 41 | -------------------------------------------------------------------------------- /ruby/irbrc.symlink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'irb/completion' 3 | require 'rubygems' 4 | 5 | IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb_history" 6 | 7 | IRB.conf[:PROMPT_MODE] = :SIMPLE 8 | 9 | IRB.conf[:AUTO_INDENT] = true 10 | 11 | class Object 12 | # list methods which aren't in superclass 13 | def local_methods(obj = self) 14 | (obj.methods - obj.class.superclass.instance_methods).sort 15 | end 16 | 17 | # print documentation 18 | # 19 | # ri 'Array#pop' 20 | # Array.ri 21 | # Array.ri :pop 22 | # arr.ri :pop 23 | def ri(method = nil) 24 | unless method && method =~ /^[A-Z]/ # if class isn't specified 25 | klass = self.kind_of?(Class) ? name : self.class.name 26 | method = [klass, method].compact.join('#') 27 | end 28 | puts `ri '#{method}'` 29 | end 30 | end 31 | 32 | # Copies the result of the last operation you ran in IRB to the system 33 | # clipboard (if you're on macOS). 34 | def cop 35 | last_value = IRB.CurrentContext.last_value 36 | %x[echo '#{last_value}' | pbcopy] 37 | "copied \`#{last_value}' to your clipboard" 38 | end 39 | 40 | def me 41 | User.find_by_email(`git config --get user.email`.chomp) 42 | end 43 | 44 | def r 45 | reload! 46 | end 47 | -------------------------------------------------------------------------------- /zsh/config.zsh: -------------------------------------------------------------------------------- 1 | export LSCOLORS="exfxcxdxbxegedabagacad" 2 | export CLICOLOR=true 3 | 4 | fpath=($ZSH/functions $fpath) 5 | 6 | autoload -U $ZSH/functions/*(:t) 7 | 8 | HISTFILE=~/.zsh_history 9 | HISTSIZE=10000 10 | SAVEHIST=10000 11 | 12 | setopt NO_BG_NICE # don't nice background tasks 13 | setopt NO_HUP 14 | setopt NO_LIST_BEEP 15 | setopt LOCAL_OPTIONS # allow functions to have local options 16 | setopt LOCAL_TRAPS # allow functions to have local traps 17 | setopt HIST_VERIFY 18 | setopt SHARE_HISTORY # share history between sessions ??? 19 | setopt EXTENDED_HISTORY # add timestamps to history 20 | setopt PROMPT_SUBST 21 | setopt CORRECT 22 | setopt COMPLETE_IN_WORD 23 | setopt IGNORE_EOF 24 | 25 | setopt APPEND_HISTORY # adds history 26 | setopt INC_APPEND_HISTORY SHARE_HISTORY # adds history incrementally and share it across sessions 27 | setopt HIST_IGNORE_ALL_DUPS # don't record dupes in history 28 | setopt HIST_REDUCE_BLANKS 29 | 30 | # don't expand aliases _before_ completion has finished 31 | # like: git comm-[tab] 32 | setopt complete_aliases 33 | 34 | bindkey '^[^[[D' backward-word 35 | bindkey '^[^[[C' forward-word 36 | bindkey '^[[5D' beginning-of-line 37 | bindkey '^[[5C' end-of-line 38 | bindkey '^[[3~' delete-char 39 | bindkey '^?' backward-delete-char 40 | -------------------------------------------------------------------------------- /macos/set-hostname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This corrects a shitty point of confusion with macOS where if you bounce 4 | # between wireless and wired connections, macOS will suddenly throw up its hands 5 | # and add a random-ass number to your hostname. Do it a couple times and you're 6 | # in like, the thousands appended to your hostname, which makes you look like a 7 | # chump when your machine is called "incredible-programmer-9390028", like 8 | # you're behind 9,390,027 other better programmers before you. Sheesh. 9 | # 10 | # Anyway, this runs in `dot` and only asks for your permission (usually TouchID) 11 | # if it actually needs to change your hostname for you, otherwise it's fast to 12 | # toss into `dot` anyway. 13 | # 14 | # None of this really matters in the big scheme of things, but it bothered me. 15 | 16 | hostname=$(scutil --get LocalHostName) 17 | 18 | # if hostname contains a hyphen and then a number, remove the hyphen and number 19 | normal_hostname=$(echo "$hostname" | sed 's/-[0-9]*$//') 20 | 21 | # if our hostname was changed by macOS, change it back 22 | if [ "$normal_hostname" != "$hostname" ]; then 23 | echo "Changing hostname from $hostname to $normal_hostname" 24 | scutil --set LocalHostName "$normal_hostname" 25 | scutil --set ComputerName "$normal_hostname" 26 | fi 27 | -------------------------------------------------------------------------------- /functions/extract: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Usage: extract 4 | # Description: extracts archived files / mounts disk images 5 | # Note: .dmg/hdiutil is macOS-specific. 6 | # 7 | # credit: http://nparikh.org/notes/zshrc.txt 8 | extract () { 9 | if [ -f $1 ]; then 10 | case $1 in 11 | *.tar.bz2) tar -jxvf $1 ;; 12 | *.tar.gz) tar -zxvf $1 ;; 13 | *.bz2) bunzip2 $1 ;; 14 | *.dmg) hdiutil mount $1 ;; 15 | *.gz) gunzip $1 ;; 16 | *.tar) tar -xvf $1 ;; 17 | *.tbz2) tar -jxvf $1 ;; 18 | *.tgz) tar -zxvf $1 ;; 19 | *.zip) unzip $1 ;; 20 | *.ZIP) unzip $1 ;; 21 | *.pax) cat $1 | pax -r ;; 22 | *.pax.Z) uncompress $1 --stdout | pax -r ;; 23 | *.rar) unrar x $1 ;; 24 | *.Z) uncompress $1 ;; 25 | *) echo "'$1' cannot be extracted/mounted via extract()" ;; 26 | esac 27 | else 28 | echo "'$1' is not a valid file" 29 | fi 30 | } 31 | -------------------------------------------------------------------------------- /bin/dot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # dot 4 | # 5 | # `dot` handles installation, updates, things like that. Run it periodically 6 | # to make sure you're on the latest and greatest. 7 | 8 | set -e 9 | 10 | parentDirectory="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd -P)" 11 | dotfilesDirectory="$(cd "$( dirname "$parentDirectory" )" && pwd -P)" 12 | 13 | displayUsageAndExit() { 14 | echo "dot -- dotfiles management" 15 | echo "" 16 | echo "Usage: dot [options]" 17 | echo "" 18 | echo "Options:" 19 | echo " -e, --edit Open dotfiles directory for editing" 20 | echo " -h, --help Show this help message and exit" 21 | exit 22 | } 23 | 24 | while test $# -gt 0; do 25 | case "$1" in 26 | "-h"|"--help") 27 | displayUsageAndExit 28 | ;; 29 | "-e"|"--edit") 30 | exec "$EDITOR" "$dotfilesDirectory" 31 | exit 32 | ;; 33 | *) 34 | echo "Invalid option: $1" 35 | displayUsageAndExit 36 | ;; 37 | esac 38 | shift 39 | done 40 | 41 | export ZSH=$HOME/.dotfiles 42 | 43 | # Update dotfiles themselves 44 | echo "› git pull" 45 | git -C $ZSH pull 46 | 47 | # Set macOS defaults 48 | $ZSH/macos/set-defaults.sh 49 | 50 | # Set macOS hostname 51 | $ZSH/macos/set-hostname.sh 52 | 53 | # Install homebrew 54 | $ZSH/homebrew/install.sh 2>&1 55 | 56 | # Upgrade homebrew 57 | echo "› brew update" 58 | brew update 59 | 60 | echo "› brew upgrade" 61 | brew upgrade 62 | 63 | # Install software 64 | echo "› script/install" 65 | $ZSH/script/install 66 | -------------------------------------------------------------------------------- /zsh/zshrc.symlink: -------------------------------------------------------------------------------- 1 | # shortcut to this dotfiles path is $ZSH 2 | export ZSH=$HOME/.dotfiles 3 | 4 | # your project folder that we can `c [tab]` to 5 | export PROJECTS=~/Code 6 | 7 | # Stash your environment variables in ~/.localrc. This means they'll stay out 8 | # of your main dotfiles repository (which may be public, like this one), but 9 | # you'll have access to them in your scripts. 10 | if [[ -a ~/.localrc ]] 11 | then 12 | source ~/.localrc 13 | fi 14 | 15 | # all of our zsh files 16 | typeset -U config_files 17 | config_files=($ZSH/**/*.zsh) 18 | 19 | # load the path files 20 | for file in ${(M)config_files:#*/path.zsh} 21 | do 22 | source $file 23 | done 24 | 25 | # load everything but the path and completion files 26 | for file in ${${config_files:#*/path.zsh}:#*/completion.zsh} 27 | do 28 | source $file 29 | done 30 | 31 | # initialize autocomplete here, otherwise functions won't be loaded 32 | autoload -U compinit 33 | compinit 34 | 35 | # load every completion after autocomplete loads 36 | for file in ${(M)config_files:#*/completion.zsh} 37 | do 38 | source $file 39 | done 40 | 41 | unset config_files 42 | 43 | # Better history 44 | # Credits to https://coderwall.com/p/jpj_6q/zsh-better-history-searching-with-arrow-keys 45 | autoload -U up-line-or-beginning-search 46 | autoload -U down-line-or-beginning-search 47 | zle -N up-line-or-beginning-search 48 | zle -N down-line-or-beginning-search 49 | bindkey "^[[A" up-line-or-beginning-search # Up 50 | bindkey "^[[B" down-line-or-beginning-search # Down 51 | -------------------------------------------------------------------------------- /macos/set-defaults.sh: -------------------------------------------------------------------------------- 1 | # Sets reasonable macOS defaults. 2 | # 3 | # Or, in other words, set shit how I like in macOS. 4 | # 5 | # The original idea (and a couple settings) were grabbed from: 6 | # https://github.com/mathiasbynens/dotfiles/blob/master/.macos 7 | # 8 | # Run ./set-defaults.sh and you'll be good to go. 9 | 10 | # Disable press-and-hold for keys in favor of key repeat. 11 | defaults write -g ApplePressAndHoldEnabled -bool false 12 | 13 | # Use AirDrop over every interface. srsly this should be a default. 14 | defaults write com.apple.NetworkBrowser BrowseAllInterfaces 1 15 | 16 | # Always open everything in Finder's list view. This is important. 17 | defaults write com.apple.Finder FXPreferredViewStyle Nlsv 18 | 19 | # Show the ~/Library folder. 20 | chflags nohidden ~/Library 21 | 22 | # Set a really fast key repeat. 23 | defaults write NSGlobalDomain KeyRepeat -int 1 24 | 25 | # Set the Finder prefs for showing a few different volumes on the Desktop. 26 | defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool true 27 | defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool true 28 | 29 | # Run the screensaver if we're in the bottom-left hot corner. 30 | defaults write com.apple.dock wvous-bl-corner -int 5 31 | defaults write com.apple.dock wvous-bl-modifier -int 0 32 | 33 | # Hide Safari's bookmark bar. 34 | defaults write com.apple.Safari.plist ShowFavoritesBar -bool false 35 | 36 | # Set up Safari for development. 37 | defaults write com.apple.Safari.SandboxBroker ShowDevelopMenu -bool true 38 | defaults write com.apple.Safari.plist IncludeDevelopMenu -bool true 39 | defaults write com.apple.Safari.plist WebKitDeveloperExtrasEnabledPreferenceKey -bool true 40 | defaults write com.apple.Safari.plist "com.apple.Safari.ContentPageGroupIdentifier.WebKit2DeveloperExtrasEnabled" -bool true 41 | defaults write NSGlobalDomain WebKitDeveloperExtras -bool true 42 | -------------------------------------------------------------------------------- /zsh/prompt.zsh: -------------------------------------------------------------------------------- 1 | autoload colors && colors 2 | # cheers, @ehrenmurdick 3 | # http://github.com/ehrenmurdick/config/blob/master/zsh/prompt.zsh 4 | 5 | if (( $+commands[git] )) 6 | then 7 | git="$commands[git]" 8 | else 9 | git="/usr/bin/git" 10 | fi 11 | 12 | git_branch() { 13 | echo $($git symbolic-ref HEAD 2>/dev/null | awk -F/ {'print $NF'}) 14 | } 15 | 16 | git_dirty() { 17 | if $(! $git status -s &> /dev/null) 18 | then 19 | echo "" 20 | else 21 | if [[ $($git status --porcelain) == "" ]] 22 | then 23 | echo "on %{$fg_bold[green]%}$(git_prompt_info)%{$reset_color%}" 24 | else 25 | echo "on %{$fg_bold[red]%}$(git_prompt_info)%{$reset_color%}" 26 | fi 27 | fi 28 | } 29 | 30 | git_prompt_info () { 31 | ref=$($git symbolic-ref HEAD 2>/dev/null) || return 32 | # echo "(%{\e[0;33m%}${ref#refs/heads/}%{\e[0m%})" 33 | echo "${ref#refs/heads/}" 34 | } 35 | 36 | # This assumes that you always have an origin named `origin`, and that you only 37 | # care about one specific origin. If this is not the case, you might want to use 38 | # `$git cherry -v @{upstream}` instead. 39 | need_push () { 40 | if [ $($git rev-parse --is-inside-work-tree 2>/dev/null) ] 41 | then 42 | number=$($git cherry -v origin/$(git symbolic-ref --short HEAD) 2>/dev/null | wc -l | bc) 43 | 44 | if [[ $number == 0 ]] 45 | then 46 | echo " " 47 | else 48 | echo " with %{$fg_bold[magenta]%}$number unpushed%{$reset_color%}" 49 | fi 50 | fi 51 | } 52 | 53 | directory_name() { 54 | echo "%{$fg_bold[cyan]%}%1/%\/%{$reset_color%}" 55 | } 56 | 57 | battery_status() { 58 | if test ! "$(uname)" = "Darwin" 59 | then 60 | exit 0 61 | fi 62 | 63 | if [[ $(sysctl -n hw.model) == *"Book"* ]] 64 | then 65 | $ZSH/bin/battery-status 66 | fi 67 | } 68 | 69 | export PROMPT=$'\n$(battery_status)in $(directory_name) $(git_dirty)$(need_push)\n› ' 70 | set_prompt () { 71 | export RPROMPT="%{$fg_bold[cyan]%}%{$reset_color%}" 72 | } 73 | 74 | precmd() { 75 | title "zsh" "%m" "%55<...<%~" 76 | set_prompt 77 | } 78 | -------------------------------------------------------------------------------- /functions/_brew: -------------------------------------------------------------------------------- 1 | #compdef brew 2 | 3 | # Brew ZSH completion function 4 | # Drop this somewhere in your $fpath (like /usr/share/zsh/site-functions) 5 | # and rename it _brew 6 | # 7 | # altered from _fink 8 | 9 | _brew_all_formulae() { 10 | formulae=(`brew search`) 11 | } 12 | 13 | _brew_installed_formulae() { 14 | installed_formulae=(`brew list`) 15 | } 16 | 17 | local -a _1st_arguments 18 | _1st_arguments=( 19 | 'install:install a formula' 20 | 'remove:remove a formula' 21 | 'search:search for a formula (/regex/ or string)' 22 | 'list:list files in a formula or not-installed formulae' 23 | 'link:link a formula' 24 | 'unlink:unlink a formula' 25 | 'home:visit the homepage of a formula or the brew project' 26 | 'info:information about a formula' 27 | 'prune:remove dead links' 28 | 'update:freshen up links' 29 | 'log:git commit log for a formula' 30 | 'create:create a new formula' 31 | 'edit:edit a formula' 32 | 'bundle:install or upgrade all dependencies in a Brewfile' 33 | ) 34 | 35 | local -a _bundle_arguments 36 | _bundle_arguments=( 37 | 'dump:write all installed casks/formulae/taps into a Brewfile' 38 | 'cleanup:uninstall all dependencies not listed in a Brewfile' 39 | 'check:check if all dependencies are installed in a Brewfile' 40 | ) 41 | 42 | local expl 43 | local -a formula installed_formulae 44 | 45 | _arguments \ 46 | '(-v --verbose)'{-v,--verbose}'[verbose]' \ 47 | '(--version)--version[version information]' \ 48 | '(--prefix)--prefix[where brew lives on this system]' \ 49 | '(--cache)--cache[brew cache]' \ 50 | '*:: :->subcmds' && return 0 51 | 52 | if (( CURRENT == 1 )); then 53 | _describe -t commands "brew subcommand" _1st_arguments 54 | return 55 | fi 56 | 57 | case "$words[1]" in 58 | list) 59 | _arguments \ 60 | '(--unbrewed)--unbrewed[files in brew --prefix not controlled by brew]' \ 61 | '1: :->forms' && return 0 62 | 63 | if [[ "$state" == forms ]]; then 64 | _brew_installed_formulae 65 | _requested installed_formulae expl 'installed formulae' compadd -a installed_formulae 66 | fi 67 | ;; 68 | install|home|log|info) 69 | _brew_all_formulae 70 | _wanted formulae expl 'all formulae' compadd -a formulae 71 | ;; 72 | remove|edit|xo) 73 | _brew_installed_formulae 74 | _wanted installed_formulae expl 'installed formulae' compadd -a installed_formulae 75 | ;; 76 | bundle) 77 | _describe 'values' _bundle_arguments 78 | ;; 79 | esac 80 | -------------------------------------------------------------------------------- /bin/git-rank-contributors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ## git-rank-contributors: a simple script to trace through the logs and 4 | ## rank contributors by the total size of the diffs they're responsible for. 5 | ## A change counts twice as much as a plain addition or deletion. 6 | ## 7 | ## Output may or may not be suitable for inclusion in a CREDITS file. 8 | ## Probably not without some editing, because people often commit from more 9 | ## than one address. 10 | ## 11 | ## git-rank-contributors Copyright 2008 William Morgan . 12 | ## This program is free software: you can redistribute it and/or modify 13 | ## it under the terms of the GNU General Public License as published by 14 | ## the Free Software Foundation, either version 3 of the License, or (at 15 | ## your option) any later version. 16 | ## 17 | ## This program is distributed in the hope that it will be useful, 18 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ## GNU General Public License for more details. 21 | ## 22 | ## You can find the GNU General Public License at: 23 | ## http://www.gnu.org/licenses/ 24 | 25 | class String 26 | def obfuscate; gsub(/@/, " at the ").gsub(/\.(\w+)(>|$)/, ' dot \1s\2') end 27 | def htmlize; gsub("&", "&").gsub("<", "<").gsub(">", ">") end 28 | # Fix for invalid encodings http://stackoverflow.com/a/24037885 29 | def clean 30 | if RUBY_VERSION >= "2.1" 31 | scrub 32 | elsif RUBY_VERSION >= "1.9" 33 | encode("UTF-16be", :invalid => :replace).encode("UTF-8") 34 | else 35 | self 36 | end 37 | end 38 | end 39 | 40 | lines = {} 41 | verbose = ARGV.delete("-v") 42 | obfuscate = ARGV.delete("-o") 43 | htmlize = ARGV.delete("-h") 44 | 45 | author = nil 46 | state = :pre_author 47 | `git log -M -C -C -p --no-color`.clean.split("\n").each do |l| 48 | case 49 | when (state == :pre_author || state == :post_author) && l =~ /Author: (.*)$/ 50 | author = $1 51 | state = :post_author 52 | lines[author] ||= 0 53 | when state == :post_author && l =~ /^\+\+\+/ 54 | state = :in_diff 55 | when state == :in_diff && l =~ /^[\+\-]/ 56 | lines[author] += 1 57 | when state == :in_diff && l =~ /^commit / 58 | state = :pre_author 59 | end 60 | end 61 | 62 | lines.sort_by { |a, c| -c }.each do |a, c| 63 | a = a.obfuscate if obfuscate 64 | a = a.htmlize if htmlize 65 | if verbose 66 | puts "#{a}: #{c} lines of diff" 67 | else 68 | puts a 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # bootstrap installs things. 4 | 5 | cd "$(dirname "$0")/.." 6 | DOTFILES_ROOT=$(pwd -P) 7 | 8 | set -e 9 | 10 | echo '' 11 | 12 | info () { 13 | printf "\r [ \033[00;34m..\033[0m ] $1\n" 14 | } 15 | 16 | user () { 17 | printf "\r [ \033[0;33m??\033[0m ] $1\n" 18 | } 19 | 20 | success () { 21 | printf "\r\033[2K [ \033[00;32mOK\033[0m ] $1\n" 22 | } 23 | 24 | fail () { 25 | printf "\r\033[2K [\033[0;31mFAIL\033[0m] $1\n" 26 | echo '' 27 | exit 28 | } 29 | 30 | setup_gitconfig () { 31 | if ! [ -f git/gitconfig.local.symlink ] 32 | then 33 | info 'setup gitconfig' 34 | 35 | git_credential='cache' 36 | if [ "$(uname -s)" == "Darwin" ] 37 | then 38 | git_credential='osxkeychain' 39 | fi 40 | 41 | user ' - What is your github author name?' 42 | read -e git_authorname 43 | user ' - What is your github author email?' 44 | read -e git_authoremail 45 | 46 | sed -e "s/AUTHORNAME/$git_authorname/g" -e "s/AUTHOREMAIL/$git_authoremail/g" -e "s/GIT_CREDENTIAL_HELPER/$git_credential/g" git/gitconfig.local.symlink.example > git/gitconfig.local.symlink 47 | 48 | success 'gitconfig' 49 | fi 50 | } 51 | 52 | 53 | link_file () { 54 | local src=$1 dst=$2 55 | 56 | local overwrite= backup= skip= 57 | local action= 58 | 59 | if [ -f "$dst" -o -d "$dst" -o -L "$dst" ] 60 | then 61 | 62 | if [ "$overwrite_all" == "false" ] && [ "$backup_all" == "false" ] && [ "$skip_all" == "false" ] 63 | then 64 | 65 | local currentSrc="$(readlink $dst)" 66 | 67 | if [ "$currentSrc" == "$src" ] 68 | then 69 | 70 | skip=true; 71 | 72 | else 73 | 74 | user "File already exists: $dst ($(basename "$src")), what do you want to do?\n\ 75 | [s]kip, [S]kip all, [o]verwrite, [O]verwrite all, [b]ackup, [B]ackup all?" 76 | read -n 1 action 77 | 78 | case "$action" in 79 | o ) 80 | overwrite=true;; 81 | O ) 82 | overwrite_all=true;; 83 | b ) 84 | backup=true;; 85 | B ) 86 | backup_all=true;; 87 | s ) 88 | skip=true;; 89 | S ) 90 | skip_all=true;; 91 | * ) 92 | ;; 93 | esac 94 | 95 | fi 96 | 97 | fi 98 | 99 | overwrite=${overwrite:-$overwrite_all} 100 | backup=${backup:-$backup_all} 101 | skip=${skip:-$skip_all} 102 | 103 | if [ "$overwrite" == "true" ] 104 | then 105 | rm -rf "$dst" 106 | success "removed $dst" 107 | fi 108 | 109 | if [ "$backup" == "true" ] 110 | then 111 | mv "$dst" "${dst}.backup" 112 | success "moved $dst to ${dst}.backup" 113 | fi 114 | 115 | if [ "$skip" == "true" ] 116 | then 117 | success "skipped $src" 118 | fi 119 | fi 120 | 121 | if [ "$skip" != "true" ] # "false" or empty 122 | then 123 | ln -s "$1" "$2" 124 | success "linked $1 to $2" 125 | fi 126 | } 127 | 128 | install_dotfiles () { 129 | info 'installing dotfiles' 130 | 131 | local overwrite_all=false backup_all=false skip_all=false 132 | 133 | for src in $(find -H "$DOTFILES_ROOT" -maxdepth 2 -name '*.symlink' -not -path '*.git*') 134 | do 135 | dst="$HOME/.$(basename "${src%.*}")" 136 | link_file "$src" "$dst" 137 | done 138 | } 139 | 140 | setup_gitconfig 141 | install_dotfiles 142 | 143 | # If we're on a Mac, let's install and setup homebrew. 144 | if [ "$(uname -s)" == "Darwin" ] 145 | then 146 | info "installing dependencies" 147 | if source bin/dot | while read -r data; do info "$data"; done 148 | then 149 | success "dependencies installed" 150 | else 151 | fail "error installing dependencies" 152 | fi 153 | fi 154 | 155 | echo '' 156 | echo ' All installed!' 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # holman does dotfiles 2 | 3 | Your dotfiles are how you personalize your system. These are mine. 4 | 5 | I was a little tired of having long alias files and everything strewn about 6 | (which is extremely common on other dotfiles projects, too). That led to this 7 | project being much more topic-centric. I realized I could split a lot of things 8 | up into the main areas I used (Ruby, git, system libraries, and so on), so I 9 | structured the project accordingly. 10 | 11 | If you're interested in the philosophy behind why projects like these are 12 | awesome, you might want to [read my post on the 13 | subject](http://zachholman.com/2010/08/dotfiles-are-meant-to-be-forked/). 14 | 15 | ## topical 16 | 17 | Everything's built around topic areas. If you're adding a new area to your 18 | forked dotfiles — say, "Java" — you can simply add a `java` directory and put 19 | files in there. Anything with an extension of `.zsh` will get automatically 20 | included into your shell. Anything with an extension of `.symlink` will get 21 | symlinked without extension into `$HOME` when you run `script/bootstrap`. 22 | 23 | ## what's inside 24 | 25 | A lot of stuff. Seriously, a lot of stuff. Check them out in the file browser 26 | above and see what components may mesh up with you. 27 | [Fork it](https://github.com/holman/dotfiles/fork), remove what you don't 28 | use, and build on what you do use. 29 | 30 | ## components 31 | 32 | There's a few special files in the hierarchy. 33 | 34 | - **bin/**: Anything in `bin/` will get added to your `$PATH` and be made 35 | available everywhere. 36 | - **topic/\*.zsh**: Any files ending in `.zsh` get loaded into your 37 | environment. 38 | - **topic/path.zsh**: Any file named `path.zsh` is loaded first and is 39 | expected to setup `$PATH` or similar. 40 | - **topic/completion.zsh**: Any file named `completion.zsh` is loaded 41 | last and is expected to setup autocomplete. 42 | - **topic/install.sh**: Any file named `install.sh` is executed when you run `script/install`. To avoid being loaded automatically, its extension is `.sh`, not `.zsh`. 43 | - **topic/\*.symlink**: Any file ending in `*.symlink` gets symlinked into 44 | your `$HOME`. This is so you can keep all of those versioned in your dotfiles 45 | but still keep those autoloaded files in your home directory. These get 46 | symlinked in when you run `script/bootstrap`. 47 | 48 | ## install 49 | 50 | Run this: 51 | 52 | ```sh 53 | git clone https://github.com/holman/dotfiles.git ~/.dotfiles 54 | cd ~/.dotfiles 55 | script/bootstrap 56 | ``` 57 | 58 | This will symlink the appropriate files in `.dotfiles` to your home directory. 59 | Everything is configured and tweaked within `~/.dotfiles`. 60 | 61 | The main file you'll want to change right off the bat is `zsh/zshrc.symlink`, 62 | which sets up a few paths that'll be different on your particular machine. 63 | 64 | `dot` is a simple script that installs some dependencies, sets sane macOS 65 | defaults, and so on. Tweak this script, and occasionally run `dot` from 66 | time to time to keep your environment fresh and up-to-date. You can find 67 | this script in `bin/`. 68 | 69 | ## bugs 70 | 71 | I want this to work for everyone; that means when you clone it down it should 72 | work for you even though you may not have `rbenv` installed, for example. That 73 | said, I do use this as _my_ dotfiles, so there's a good chance I may break 74 | something if I forget to make a check for a dependency. 75 | 76 | If you're brand-new to the project and run into any blockers, please 77 | [open an issue](https://github.com/holman/dotfiles/issues) on this repository 78 | and I'd love to get it fixed for you! 79 | 80 | ## thanks 81 | 82 | I forked [Ryan Bates](http://github.com/ryanb)' excellent 83 | [dotfiles](http://github.com/ryanb/dotfiles) for a couple years before the 84 | weight of my changes and tweaks inspired me to finally roll my own. But Ryan's 85 | dotfiles were an easy way to get into bash customization, and then to jump ship 86 | to zsh a bit later. A decent amount of the code in these dotfiles stem or are 87 | inspired from Ryan's original project. 88 | -------------------------------------------------------------------------------- /bin/git-wtf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | HELP = < 54 | .git-wtfrc" and edit it. The config file is a YAML file that specifies the 55 | integration branches, any branches to ignore, and the max number of commits to 56 | display when --all-commits isn't used. git-wtf will look for a .git-wtfrc file 57 | starting in the current directory, and recursively up to the root. 58 | 59 | IMPORTANT NOTE: all local branches referenced in .git-wtfrc must be prefixed 60 | with heads/, e.g. "heads/master". Remote branches must be of the form 61 | remotes//. 62 | EOS 63 | 64 | COPYRIGHT = <. 66 | This program is free software: you can redistribute it and/or modify it 67 | under the terms of the GNU General Public License as published by the Free 68 | Software Foundation, either version 3 of the License, or (at your option) 69 | any later version. 70 | 71 | This program is distributed in the hope that it will be useful, but WITHOUT 72 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 73 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 74 | more details. 75 | 76 | You can find the GNU General Public License at: http://www.gnu.org/licenses/ 77 | EOS 78 | 79 | require 'yaml' 80 | CONFIG_FN = ".git-wtfrc" 81 | 82 | class Numeric; def pluralize s; "#{to_s} #{s}" + (self != 1 ? "s" : "") end end 83 | 84 | if ARGV.delete("--help") || ARGV.delete("-h") 85 | puts USAGE 86 | exit 87 | end 88 | 89 | ## poor man's trollop 90 | $long = ARGV.delete("--long") || ARGV.delete("-l") 91 | $short = ARGV.delete("--short") || ARGV.delete("-s") 92 | $all = ARGV.delete("--all") || ARGV.delete("-a") 93 | $all_commits = ARGV.delete("--all-commits") || ARGV.delete("-A") 94 | $dump_config = ARGV.delete("--dump-config") 95 | $key = ARGV.delete("--key") || ARGV.delete("-k") 96 | $show_relations = ARGV.delete("--relations") || ARGV.delete("-r") 97 | ARGV.each { |a| abort "Error: unknown argument #{a}." if a =~ /^--/ } 98 | 99 | ## search up the path for a file 100 | def find_file fn 101 | while true 102 | return fn if File.exist? fn 103 | fn2 = File.join("..", fn) 104 | return nil if File.expand_path(fn2) == File.expand_path(fn) 105 | fn = fn2 106 | end 107 | end 108 | 109 | want_color = `git config color.wtf` 110 | want_color = `git config color.ui` if want_color.empty? 111 | $color = case want_color.chomp 112 | when "true"; true 113 | when "auto"; $stdout.tty? 114 | end 115 | 116 | def red s; $color ? "\033[31m#{s}\033[0m" : s end 117 | def green s; $color ? "\033[32m#{s}\033[0m" : s end 118 | def yellow s; $color ? "\033[33m#{s}\033[0m" : s end 119 | def cyan s; $color ? "\033[36m#{s}\033[0m" : s end 120 | def grey s; $color ? "\033[1;30m#{s}\033[0m" : s end 121 | def purple s; $color ? "\033[35m#{s}\033[0m" : s end 122 | 123 | ## the set of commits in 'to' that aren't in 'from'. 124 | ## if empty, 'to' has been merged into 'from'. 125 | def commits_between from, to 126 | if $long 127 | `git log --pretty=format:"- %s [#{yellow "%h"}] (#{purple "%ae"}; %ar)" #{from}..#{to}` 128 | else 129 | `git log --pretty=format:"- %s [#{yellow "%h"}]" #{from}..#{to}` 130 | end.split(/[\r\n]+/) 131 | end 132 | 133 | def show_commits commits, prefix=" " 134 | if commits.empty? 135 | puts "#{prefix} none" 136 | else 137 | max = $all_commits ? commits.size : $config["max_commits"] 138 | max -= 1 if max == commits.size - 1 # never show "and 1 more" 139 | commits[0 ... max].each { |c| puts "#{prefix}#{c}" } 140 | puts grey("#{prefix}... and #{commits.size - max} more (use -A to see all).") if commits.size > max 141 | end 142 | end 143 | 144 | def ahead_behind_string ahead, behind 145 | [ahead.empty? ? nil : "#{ahead.size.pluralize 'commit'} ahead", 146 | behind.empty? ? nil : "#{behind.size.pluralize 'commit'} behind"]. 147 | compact.join("; ") 148 | end 149 | 150 | def widget merged_in, remote_only=false, local_only=false, local_only_merge=false 151 | left, right = case 152 | when remote_only; %w({ }) 153 | when local_only; %w{( )} 154 | else %w([ ]) 155 | end 156 | middle = case 157 | when merged_in && local_only_merge; green("~") 158 | when merged_in; green("x") 159 | else " " 160 | end 161 | print left, middle, right 162 | end 163 | 164 | def show b 165 | have_both = b[:local_branch] && b[:remote_branch] 166 | 167 | pushc, pullc, oosync = if have_both 168 | [x = commits_between(b[:remote_branch], b[:local_branch]), 169 | y = commits_between(b[:local_branch], b[:remote_branch]), 170 | !x.empty? && !y.empty?] 171 | end 172 | 173 | if b[:local_branch] 174 | puts "Local branch: " + green(b[:local_branch].sub(/^heads\//, "")) 175 | 176 | if have_both 177 | if pushc.empty? 178 | puts "#{widget true} in sync with remote" 179 | else 180 | action = oosync ? "push after rebase / merge" : "push" 181 | puts "#{widget false} NOT in sync with remote (you should #{action})" 182 | show_commits pushc unless $short 183 | end 184 | end 185 | end 186 | 187 | if b[:remote_branch] 188 | puts "Remote branch: #{cyan b[:remote_branch]} (#{b[:remote_url]})" 189 | 190 | if have_both 191 | if pullc.empty? 192 | puts "#{widget true} in sync with local" 193 | else 194 | action = pushc.empty? ? "merge" : "rebase / merge" 195 | puts "#{widget false} NOT in sync with local (you should #{action})" 196 | show_commits pullc unless $short 197 | end 198 | end 199 | end 200 | 201 | puts "\n#{red "WARNING"}: local and remote branches have diverged. A merge will occur unless you rebase." if oosync 202 | end 203 | 204 | def show_relations b, all_branches 205 | ibs, fbs = all_branches.partition { |name, br| $config["integration-branches"].include?(br[:local_branch]) || $config["integration-branches"].include?(br[:remote_branch]) } 206 | if $config["integration-branches"].include? b[:local_branch] 207 | puts "\nFeature branches:" unless fbs.empty? 208 | fbs.each do |name, br| 209 | next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch]) 210 | next if br[:ignore] 211 | local_only = br[:remote_branch].nil? 212 | remote_only = br[:local_branch].nil? 213 | name = if local_only 214 | purple br[:name] 215 | elsif remote_only 216 | cyan br[:name] 217 | else 218 | green br[:name] 219 | end 220 | 221 | ## for remote_only branches, we'll compute wrt the remote branch head. otherwise, we'll 222 | ## use the local branch head. 223 | head = remote_only ? br[:remote_branch] : br[:local_branch] 224 | 225 | remote_ahead = b[:remote_branch] ? commits_between(b[:remote_branch], head) : [] 226 | local_ahead = b[:local_branch] ? commits_between(b[:local_branch], head) : [] 227 | 228 | if local_ahead.empty? && remote_ahead.empty? 229 | puts "#{widget true, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is merged in" 230 | elsif local_ahead.empty? 231 | puts "#{widget true, remote_only, local_only, true} #{name} merged in (only locally)" 232 | else 233 | behind = commits_between head, (br[:local_branch] || br[:remote_branch]) 234 | ahead = remote_only ? remote_ahead : local_ahead 235 | puts "#{widget false, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is NOT merged in (#{ahead_behind_string ahead, behind})" 236 | show_commits ahead unless $short 237 | end 238 | end 239 | else 240 | puts "\nIntegration branches:" unless ibs.empty? # unlikely 241 | ibs.sort_by { |v, br| v }.each do |v, br| 242 | next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch]) 243 | next if br[:ignore] 244 | local_only = br[:remote_branch].nil? 245 | remote_only = br[:local_branch].nil? 246 | name = remote_only ? cyan(br[:name]) : green(br[:name]) 247 | 248 | ahead = commits_between v, (b[:local_branch] || b[:remote_branch]) 249 | if ahead.empty? 250 | puts "#{widget true, local_only} merged into #{name}" 251 | else 252 | #behind = commits_between b[:local_branch], v 253 | puts "#{widget false, local_only} NOT merged into #{name} (#{ahead.size.pluralize 'commit'} ahead)" 254 | show_commits ahead unless $short 255 | end 256 | end 257 | end 258 | end 259 | 260 | #### EXECUTION STARTS HERE #### 261 | 262 | ## find config file and load it 263 | $config = { "integration-branches" => %w(heads/master heads/next heads/edge), "ignore" => [], "max_commits" => 5 }.merge begin 264 | fn = find_file CONFIG_FN 265 | if fn && (h = YAML::load_file(fn)) # yaml turns empty files into false 266 | h["integration-branches"] ||= h["versions"] # support old nomenclature 267 | h 268 | else 269 | {} 270 | end 271 | end 272 | 273 | if $dump_config 274 | puts $config.to_yaml 275 | exit 276 | end 277 | 278 | ## first, index registered remotes 279 | remotes = `git config --get-regexp ^remote\.\*\.url`.split(/[\r\n]+/).inject({}) do |hash, l| 280 | l =~ /^remote\.(.+?)\.url (.+)$/ or next hash 281 | hash[$1] ||= $2 282 | hash 283 | end 284 | 285 | ## next, index followed branches 286 | branches = `git config --get-regexp ^branch\.`.split(/[\r\n]+/).inject({}) do |hash, l| 287 | case l 288 | when /branch\.(.*?)\.remote (.+)/ 289 | name, remote = $1, $2 290 | 291 | hash[name] ||= {} 292 | hash[name].merge! :remote => remote, :remote_url => remotes[remote] 293 | when /branch\.(.*?)\.merge ((refs\/)?heads\/)?(.+)/ 294 | name, remote_branch = $1, $4 295 | hash[name] ||= {} 296 | hash[name].merge! :remote_mergepoint => remote_branch 297 | end 298 | hash 299 | end 300 | 301 | ## finally, index all branches 302 | remote_branches = {} 303 | `git show-ref`.split(/[\r\n]+/).each do |l| 304 | sha1, ref = l.chomp.split " refs/" 305 | 306 | if ref =~ /^heads\/(.+)$/ # local branch 307 | name = $1 308 | next if name == "HEAD" 309 | branches[name] ||= {} 310 | branches[name].merge! :name => name, :local_branch => ref 311 | elsif ref =~ /^remotes\/(.+?)\/(.+)$/ # remote branch 312 | remote, name = $1, $2 313 | remote_branches["#{remote}/#{name}"] = true 314 | next if name == "HEAD" 315 | ignore = !($all || remote == "origin") 316 | 317 | branch = name 318 | if branches[name] && branches[name][:remote] == remote 319 | # nothing 320 | else 321 | name = "#{remote}/#{branch}" 322 | end 323 | 324 | branches[name] ||= {} 325 | branches[name].merge! :name => name, :remote => remote, :remote_branch => "#{remote}/#{branch}", :remote_url => remotes[remote], :ignore => ignore 326 | end 327 | end 328 | 329 | ## assemble remotes 330 | branches.each do |k, b| 331 | next unless b[:remote] && b[:remote_mergepoint] 332 | b[:remote_branch] = if b[:remote] == "." 333 | b[:remote_mergepoint] 334 | else 335 | t = "#{b[:remote]}/#{b[:remote_mergepoint]}" 336 | remote_branches[t] && t # only if it's still alive 337 | end 338 | end 339 | 340 | show_dirty = ARGV.empty? 341 | targets = if ARGV.empty? 342 | [`git symbolic-ref HEAD`.chomp.sub(/^refs\/heads\//, "")] 343 | else 344 | ARGV.map { |x| x.sub(/^heads\//, "") } 345 | end.map { |t| branches[t] or abort "Error: can't find branch #{t.inspect}." } 346 | 347 | targets.each do |t| 348 | show t 349 | show_relations t, branches if $show_relations || t[:remote_branch].nil? 350 | end 351 | 352 | modified = show_dirty && `git ls-files -m` != "" 353 | uncommitted = show_dirty && `git diff-index --cached HEAD` != "" 354 | 355 | if $key 356 | puts 357 | puts KEY 358 | end 359 | 360 | puts if modified || uncommitted 361 | puts "#{red "NOTE"}: working directory contains modified files." if modified 362 | puts "#{red "NOTE"}: staging area contains staged but uncommitted files." if uncommitted 363 | 364 | # the end! --------------------------------------------------------------------------------