commit de290ab43d4a684cfc272c76ddaa052fbde61aa9
parent 83cd1d31ba8f1c0047ab525bbe7bd49ef7c12697
Author: Lucas Burns <burnsac@me.com>
Date:   Thu, 10 Mar 2022 17:54:46 -0600

chore: partial conversion to zsh

Diffstat:
MMakefile | 6+++---
Mfadd | 74++++++++++++++++++++++++++++++++++++++------------------------------------
Mfcheckout | 127+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mfedit | 73++++++++++++++++++++++++++++++++++++++-----------------------------------
Afgit_helper | 356+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dfgit_helper.sh | 315-------------------------------------------------------------------------------
Mflog | 114+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mfreset | 76++++++++++++++++++++++++++++++++++++++++------------------------------------
Mfstash | 102++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mfstat | 24++++++++++++++----------
Mfuntrack | 33+++++++++++++++++++--------------
Mfzgrep | 15++++++++++-----
Mpreview.sh | 7+++++--
13 files changed, 702 insertions(+), 620 deletions(-)

diff --git a/Makefile b/Makefile @@ -32,8 +32,8 @@ install: @echo "funtrack installed successfully" @install -vm755 fzgrep "$(DESTDIR)/fzgrep" @echo "fzgrep installed successfully" - @install -vm755 fgit_helper.sh "$(DESTDIR)/fgit_helper.sh" - @echo "fgit_helper.sh installed successfully" + @install -vm755 fgit_helper "$(DESTDIR)/fgit_helper" + @echo "fgit_helper installed successfully" @install -vm755 preview.sh "$(DESTDIR)/preview.sh" @echo "preview.sh installed successfully" @echo @@ -52,5 +52,5 @@ uninstall: "$(DESTDIR)/fstat" \ "$(DESTDIR)/funtrack" \ "$(DESTDIR)/fzgrep" \ - "$(DESTDIR)/fgit_helper.sh" \ + "$(DESTDIR)/fgit_helper" \ "$(DESTDIR)/preview.sh" diff --git a/fadd b/fadd @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: stage the selected file to git bare repo # # @params @@ -12,23 +15,26 @@ # -f|--file: select a file in PWD to stage # -d|--dir: select a directory in PWD to stage -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: fadd [-h] [-f] [-d] ... +function usage() { + builtin print -P -- "\ +%F{1}%BUsage%f%b: + %F{2}fadd%f [%F{13}-h%f] [%F{13}-f%f] [%F{13}-d%f] ... Select files/directories or modified files through fzf. Stage the selected file to the dotfile gitbare repo. -Default: list all modified files and stage the selected files. +%F{4}Default%f: list all modified files and stage the selected files. -Optional arguments: - -h, --help\t\tshow this help message and exit. - -f, --file\t\tselect files in current directory and stage the selected files. - -d, --dir\t\tselect folders in current directory and stage the selected folders." +%F{1}%BOptions%f%b: + %F{13}-h%f, %F{13}--help%f Show this help message and exit + %F{13}-f%f, %F{13}--file%f Select files in current directory and stage the selected files + %F{13}-d%f, %F{13}--dir%f Select folders in current directory and stage the selected folders" } ####################################### @@ -37,36 +43,32 @@ Optional arguments: # $1: array of files to stage ####################################### -stage_file() { +function stage_file() { local -a files && files=("$@") - [[ "${#files[@]}" -eq 0 ]] && { printf "%s" "$(tput setaf 1)Nothing added$(tput sgr0)"; exit 1; } - git add "${files[@]}" + (( $#files == 0 )) && { print -Pr -- "%F{1}Nothing added%f"; exit 1; } + command git add "${files[@]}" } -stage_type="modified" -typeset -a selected_files && selected_files=() +local stage_type="modified" +local -a selected_files -while [[ "$#" -gt 0 ]]; do +while (( $# )) { case "$1" in - -f|--file) stage_type="file"; shift ;; - -d|--dir) stage_type="dir"; shift ;; - -h|--help) usage && exit 0 ;; - *) echo "Invalid option: $1" >&2 && usage && exit 1 ;; + (-f|--file) stage_type="file"; shift ;; + (-d|--dir) stage_type="dir"; shift ;; + (-h|--help) usage && exit 0 ;; + (*) print::error "Invalid option: $1" && { usage; exit 1 } ;; esac -done +} -while IFS= read -r line; do - selected_files+=("${line}") -done < <( - if [[ "${stage_type}" == "file" ]]; then - search_file 'f' - elif [[ "${stage_type}" == "dir" ]]; then - search_file 'd' - else - get_modified_file "select files to stage" "unstaged" - fi -) +if [[ $stage_type = file ]] { + selected_files=( ${(@f)"$(search_file f)"} ) +} elif [[ $stage_type = dir ]] { + selected_files=( ${(@f)"$(search_file d)"} ) +} else { + selected_files=( ${(@f)"$(get_modified_file 'Select files to stage' 'unstaged')"} ) +} -stage_file "${selected_files[@]}" +stage_file "$selected_files[@]" -[[ "${#selected_files[@]}" -ne 0 ]] && git status -sb +(( $#selected_files )) && command git status -sb diff --git a/fcheckout b/fcheckout @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: checkout files/commit/branches using fzf # # @params @@ -16,77 +19,79 @@ # -b|--branch: search branch and checkout branch # -c|--commit: search commit and checkout commit -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: fcheckout [-h] [-s] [-b] [-c] [-y] ... +function usage() { + builtin print -P -- "\ +%F{1}%BUsage%f%b: + %F{2}fcheckout%f [%F{13}-h%f] [%F{13}-s%f] [%F{13}-b%f] [%F{13}-c%f] [%F{13}-y%f] ... -Select files/commit/branch through fzf and checkout the selected objects. -Files: checkout the version in HEAD or in a specific commit (reset files content back to the selected commit). -Branch: switch to the selected branch. -Commit: switch to a specific commit. +%F{11}Select%f files/commit/branch through fzf and checkout the selected objects. +%F{11}Files%f: checkout version in %F{2}HEAD%f or in a specific commit (reset file content to commit). +%F{11}Branch%f: switch to the selected branch. +%F{11}Commit%f: switch to a specific commit. -Default: list all modified files and reset selected files back to HEAD. +%F{4}Default%f: list all modified files and reset selected files back to HEAD. -Optional arguments: - -h, --help\t\tshow this help message and exit. - -s, --select\t\tlist all tracked files and select a commit to checkout the selected files. - -b, --branch\t\tlist all branch and checkout/switch the selected branch. - -c, --commit\t\tlist all commits and checkout selected commit. - -y, --yes\t\tacknowledge all actions that will be taken and skip confirmation." +%F{1}%BOptions%f%b: + %F{13}-h%f, %F{13}--help%f Show this help message and exit + %F{13}-s%f, %F{13}--select%f List all tracked files and select a commit to checkout the selected files + %F{13}-b%f, %F{13}--branch%f List all branch and checkout/switch the selected branch + %F{13}-c%f, %F{13}--commit%f List all commits and checkout selected commit + %F{13}-y%f, %F{13}--yes%f Acknowledge all actions that will be taken and skip confirmation" } +local -a selected_files +local action_type confirm selected_commit selected_branch action_type="modified" -typeset -a selected_files && selected_files=() -confirm="" -selected_commit="" -selected_branch="" -while [[ "$#" -gt 0 ]]; do +while (( $# )) { case "$1" in - -s|--select) action_type="select"; shift ;; - -b|--branch) action_type="branch"; shift ;; - -c|--commit) action_type="commit"; shift ;; - -y|--yes) confirm="y"; shift ;; - -h|--help) usage && exit 0 ;; - *) echo "Invalid option: $1" >&2 && usage && exit 1;; + (-s|--select) action_type="select"; shift ;; + (-b|--branch) action_type="branch"; shift ;; + (-c|--commit) action_type="commit"; shift ;; + (-y|--yes) confirm="y"; shift ;; + (-h|--help) usage && exit 0 ;; + (*) print::error "Invalid option: $1" && { usage; exit 1 } ;; esac -done +} -if [[ "${action_type}" == "branch" ]]; then - # checkout branch - selected_branch=$(get_branch 'select a branch to checkout') - [[ -z "${selected_branch}" ]] && exit 1 - git checkout "${selected_branch}" -elif [[ "${action_type}" == "commit" ]]; then - # checkout commit - selected_commit=$(get_commit 'select a commit to checkout') +if [[ "$action_type" = branch ]]; then + # Checkout branch + selected_branch=$(get_branch 'Select a branch to checkout') + [[ -z "$selected_branch" ]] && exit 1 + command git checkout "${selected_branch}" + +elif [[ "$action_type" = "commit" ]]; then + # Checkout commit + selected_commit=$(get_commit 'Select a commit to checkout') [[ -z "${selected_commit}" ]] && exit 1 - git checkout "${selected_commit}" -elif [[ "${action_type}" == "modified" ]]; then - # checkout modified file back to version in HEAD - while IFS= read -r line; do - selected_files+=("${line}") - done < <(get_modified_file 'select files to checkout version in HEAD') - [[ "${#selected_files[@]}" -eq 0 ]] && exit 1 - [[ -z "${confirm}" ]] && echo "(dryrun) git checkout --" "${selected_files[@]}" - [[ -z "${confirm}" ]] && confirm=$(get_confirmation "Confirm?") - [[ "${confirm}" != 'y' ]] && exit 1 - git checkout -- "${selected_files[@]}" -elif [[ "${action_type}" == "select" ]]; then - # checkout selected files to a selected commit - while IFS= read -r line; do - selected_files+=("${line}") - done < <(get_git_file 'select files to checkout to previous commit') - [[ "${#selected_files[@]}" -eq 0 ]] && exit 1 - # continue select a commit and then checkout the file back to the selected commit + command git checkout "${selected_commit}" + +elif [[ "$action_type" = modified ]]; then + # Checkout modified file back to version in HEAD + selected_files=( ${(@f)"$(get_modified_file 'select files to checkout version in HEAD')"} ) + (( $#selected_files )) || exit 1 + [[ -z "$confirm" ]] && \ + builtin print -P "(%F{3}dryrun%f) %F{2}git%f checkout --" "${selected_files[@]}" + [[ -z "$confirm" ]] && confirm=$(get_confirmation "Confirm?") + [[ "$confirm" != y ]] && exit 1 + command git checkout -- "$selected_files[@]" + +elif [[ "$action_type" = select ]]; then + # Checkout selected files to a selected commit + selected_files=( ${(@f)"$(get_git_file 'select files to checkout to previous commit')"} ) + (( $#selected_files )) || exit 1 + # Continue select a commit and then checkout the file back to the selected commit selected_commit=$(get_commit 'select the target commit' "${selected_files[@]}") - [[ -z "${selected_commit}" ]] && exit 1 - [[ -z "${confirm}" ]] && echo "(dryrun) git checkout ${selected_commit} --" "${selected_files[@]}" - [[ -z "${confirm}" ]] && confirm=$(get_confirmation "Confirm?") - [[ "${confirm}" != 'y' ]] && exit 0 - git checkout "${selected_commit}" "${selected_files[@]}" + [[ -z "$selected_commit" ]] && exit 1 + [[ -z "$confirm" ]] && \ + builtin print -P "(%F{3}dryrun%f) %F{2}git%f checkout ${selected_commit} --" "${selected_files[@]}" + [[ -z "$confirm" ]] && confirm=$(get_confirmation "Confirm?") + [[ "$confirm" != y ]] && exit 0 + command git checkout "$selected_commit" "$selected_files[@]" fi diff --git a/fedit b/fedit @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: interactive menu to choose file/commit to edit # # @params @@ -13,51 +16,51 @@ # -c|--commit: edit commit using interactive rebase # -h|--help: show helpe message and exit -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: fedit [-h] [-m] [-c] ... +function usage() { + builtin print -P "\ +%F{1}%BUsage%f%b: + %F{2}fedit%f [%F{13}-h%f] [%F{13}-m%f] [%F{13}-c%f] ... -Select files/commits through fzf and edit selected files/commits in EDITOR. +%F{11}Select%f files/commits through fzf and edit selected files/commits in EDITOR. -Default: list all tracked dotfiles and edit the selected files. +%F{4}Default%f: list all tracked dotfiles and edit the selected files. -Optional arguments: - -h, --help\t\tshow this help message and exit. - -m, --modified\tonly list and edit selected modified files. - -c, --commit\t\tlist commit and edit the selected commit through interactive rebase." +%F{1}%BOptions%f%b: + %F{13}-h%f, %F{13}--help%f Show this help message and exit + %F{13}-m%f, %F{13}--modified%f Only list and edit selected modified files + %F{13}-c%f, %F{13}--commit%f List commit and edit the selected commit through interactive rebase" } +local -a selected_files +local edit_type selected_commit edit_type="all" -typeset -a selected_files && selected_files=() -selected_commit="" -while [[ "$#" -gt 0 ]]; do +while (( $# )) { case "$1" in - -m|--modified) edit_type="modified"; shift ;; - -c|--commit) edit_type="commit"; shift ;; - -h|--help) usage && exit 0 ;; - *) echo "Invalid option: $1" >&2 && usage && exit 1 ;; + (-m|--modified) edit_type="modified"; shift ;; + (-c|--commit) edit_type="commit"; shift ;; + (-h|--help) usage; exit 0 ;; + (*) print::error "Invalid option: $1" && { usage; exit 1 } ;; esac -done +} -if [[ "${edit_type}" == "commit" ]]; then +if [[ "$edit_type" == commit ]]; then selected_commit=$(get_commit "select a commit to edit") - [[ -z "${selected_commit}" ]] && exit 1 - git rebase -i "${selected_commit}"~ + [[ -z "$selected_commit" ]] && exit 1 + command git rebase -i "$selected_commit"~ else - while IFS= read -r line; do - selected_files+=("${line}") - done < <( - if [[ "${edit_type}" == "modified" ]]; then - get_modified_file "select files to edit" - else - get_git_file "select files to edit" - fi - ) - [[ "${#selected_files[@]}" -eq 0 ]] && exit 1 - exec "${EDITOR}" "${selected_files[@]}" + if [[ "$edit_type" = modified ]] { + selected_files=( ${(@f)"$(get_modified_file 'select files to edit')"} ) + } else { + selected_files=( ${(@f)"$(get_git_file 'select files to edit')"}) + } + + (( $#selected_files )) || exit 1 + exec "$EDITOR" "$selected_files[@]" fi diff --git a/fgit_helper b/fgit_helper @@ -0,0 +1,356 @@ +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + +emulate -LR zsh +setopt warncreateglobal noshortloops typesetsilent extendedglob + +function print::warning() { print -Pru2 -- "%F{3}[WARNING]%f: $*"; } +function print::error() { print -Pru2 -- "%F{1}%B[ERROR]%f%b: $*"; } + +zmodload -Fa zsh/parameter p:commands || { + print::error "Unable to load %F{2}zsh/paramter%f. Exiting..." + exit 1 +} + +####################################### +########## SETTING VARIABLES ########## +####################################### +() { + typeset -g \ + FZFGIT_TREE="$(command git rev-parse --show-toplevel)" \ + FZFGIT_DIFF_PAGER="${FZFGIT_DIFF_PAGER:-$(git config core.pager || print -r -- 'cat')}" \ + FZFGIT_LOG_FMT=${FZFGIT_LOG_FMT:-%C(auto)%h%d %s %C(black)%C(bold)%cr%Creset} \ + EDITOR=${EDITOR:-${VISUAL:-vim}} \ + FZFGIT_VERSION="v1.3" + + local fd=${${${:-=fd}:c}:A} + [[ -x "$fd" ]] && typeset -g fzfgit_find="$fd" || typeset -g fzfgit_find="find" + + [[ -z "${FZF_DEFAULT_OPTS}" ]] && export FZF_DEFAULT_OPTS='--cycle' + + if [[ -z "${FZFGIT_KEY}" ]]; then + typeset -g FZFGIT_KEY=" + --bind='ctrl-a:toggle-all' + --bind='ctrl-b:execute(bat --paging=always -f {+})' + --bind='ctrl-y:execute-silent(echo {+} | pbcopy)' + --bind='ctrl-e:execute(echo {+} | xargs -o $EDITOR)' + --bind='ctrl-k:preview-up' + --bind='ctrl-j:preview-down' + --bind='alt-j:jump' + --bind='alt-0:top' + --bind='ctrl-s:toggle-sort' + --bind='?:toggle-preview' + " + fi + + FZF_DEFAULT_OPTS=" + $FZF_DEFAULT_OPTS + --ansi + --cycle + --exit-0 + $FZFGIT_DEFAULT_OPTS + $FZFGIT_KEY + " + + (( ! $COLUMNS )) && COLUMNS=${(s: :)$(stty size < /dev/tty)[2]} + (( $COLUMNS < 80 )) && FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS --preview-window=hidden" +} + +####################################### +# determine to set multi selection or not +# Globals: +# ${FZF_DEFAULT_OPTS}: fzf options +# Arguments: +# $1: if exists, disable multi, set single +####################################### +function set_fzf_multi() { + local no_multi="$1" + [[ -z "$no_multi" ]] && { + export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --multi" + } || export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --no-multi" +} + +####################################### +# Helper function to get user confirmation +# Globals: +# None +# Locals: +# ${confirm}: user confirmation status +# Arguments: +# $1: confirmation message to show during confirm +# Outputs: +# ${confirm}: y or n indicating user response +####################################### +function get_confirmation() { + local confirm message="${1:-Confirm?}" + # Must be printed to stderr for other functions accepting stdout + builtin print -Pru2 -- "%F{4}$message%f" + builtin read -rq '?[Y/n]? ' confirm && { + notify-send "before: $confirm" + builtin print -r -- "${confirm}" + } +} + + +####################################### +# let user select a commit interactively +# credit to forgit for the git log format +# Arguments: +# $1: the helper message to display in the fzf header +# $2: files to show diff against HEAD +# Outputs: +# the selected commit 6 char code +# e.g. b60b330 +####################################### +function get_commit() { + # TODO: Maybe one off + local header="${1:-Select a commit}" + local -a files=( ${@[2,-1]} ) + if (( $#files )) { + command git log \ + --oneline \ + --color=always \ + --decorate=short \ + | command fzf \ + --header="$header" \ + --no-multi \ + --preview "echo {} \ + | awk '{print \$1}' \ + | xargs -I __ git \ + diff --color=always __ ${files[*]} \ + | ${FZFGIT_DIFF_PAGER}" \ + | awk '{print $1}' + } else { + command git log \ + --color=always \ + --format='%C(auto)%h%d %s %C(black)%C(bold)%cr' \ + | command fzf \ + --header="$header" \ + --no-multi \ + --preview "echo {} \ + | awk '{print \$1}' \ + | xargs -I __ git \ + show --color=always __ \ + | ${FZFGIT_DIFF_PAGER}" \ + | awk '{print $1}' + } +} + +####################################### +# let user select a branch interactively +# Arguments: +# $1: the helper message to display in the fzf header +# Outputs: +# the selected branch name +# e.g. master +####################################### +function get_branch() { + local header="${1:-Select a branch}" + command git branch -a \ + | command awk '{ + if ($0 ~ /\*.*\(HEAD.*/) { + gsub(/\* /, "", $0) + print "\033[32m" $0 "\033[0m" + } else if (match($1, "\\*") != 0) { + print "\033[32m" $2 "\033[0m" + } else if ($0 ~ /^[ \t]+remotes\/.*/) { + gsub(/[ \t]+/, "", $0) + print "\033[31m" $0 "\033[0m" + } else { + gsub(/^[ \t]+/, "", $0) + print $0 + } + }' \ + | command fzf --no-multi --header="$header" \ + --preview="echo {} \ + | awk '{ + if (\$0 ~ /.*HEAD.*/) { + print \"HEAD\" + } else { + print \$0 + } + }' \ + | xargs -I __ git \ + log --color=always --color=always --format='%C(auto)%h%d %s %C(black)%C(bold)%cr' __" +} + +####################################### +# let user select a git tracked file interactively +# Arguments: +# $1: the helper message to display in the fzf header +# $2: print option, values (full|raw) +# $3: if exist, don't do multi selection, do single +# Outputs: +# the selected file path +# e.g.$HOME/.config/nvim/init.vim +####################################### +function get_git_file() { + # TODO: Why is this here? + local mydir=${0:A:h} + local header="${1:-Select tracked file}" + local print_opt="${2:-full}" + set_fzf_multi "$3" + command git ls-files \ + --full-name \ + --directory "$FZFGIT_TREE" \ + | fzf \ + --header="$header" \ + --preview "preview.sh $FZFGIT_TREE/{}" \ + | command awk -v home="$FZFGIT_TREE" -v print_opt="$print_opt" '{ + if (print_opt == "full") { + print home "/" $0 + } else { + print $0 + } + }' +} + +####################################### +# let user select a modified file interactively +# Arguments: +# $1: the helper message to display in the fzf header +# $2: display mode of modified files. +# default: true +# all: display all modified, include staged and unstaged +# staged: display only staged files +# unstaged: display only unstaged files +# $3: output_format +# default: name +# name: formatted name of the file +# raw: raw file name with status +# $4: if exists, don't do multi selection, do single +# Outputs: +# the selected file path +# e.g.$HOME/.config/nvim/init.vim +####################################### +function get_modified_file() { + local header="${1:-Select a modified file}" + local display_mode="${2:-all}" + local output_format="${3:-name}" + set_fzf_multi "$4" + command git status --porcelain \ + | awk -v display_mode="${display_mode}" '{ + if ($0 ~ /^[[[:alpha:]]{2}.*$/) { + print "\033[32m" substr($0, 1, 1) "\033[31m" substr($0, 2) "\033[0m" + } else if ($0 ~ /^[A-Za-z][ \t].*$/) { + if (display_mode == "all" || display_mode == "staged") { + print "\033[32m" $0 "\033[0m" + } + } else { + if (display_mode == "all" || display_mode == "unstaged") { + print "\033[31m" $0 "\033[0m" + } + } + }' \ + | command fzf --header="$header" --preview "echo {} \ + | awk '{sub(\$1 FS,\"\"); print \$0}' \ + | xargs -I __ git \ + diff HEAD --color=always -- $FZFGIT_TREE/__ \ + | $FZFGIT_DIFF_PAGER" \ + | awk -v home="$FZFGIT_TREE" -v format="$output_format" '{ + if (format == "name") { + $1="" + gsub(/^[ \t]/, "", $0) + gsub(/"/, "", $0) + print home "/" $0 + } else { + print $0 + } + }' +} + +####################################### +# let user select a stash interactively +# Arguments: +# $1: the help message to display in header +# $2: if exists, don't do multi select, only allow single selection +# Outputs: +# the selected stash identifier +# e.g. stash@{0} +####################################### +function get_stash() { + local header="${1:-select a stash}" + set_fzf_multi "$2" + command git \ + stash list \ + | command fzf --header="${header}" --preview "echo {} \ + | awk '{ + gsub(/:/, \"\", \$1) + print \$1 + }' \ + | xargs -I __ git \ + stash show -p __ --color=always \ + | $FZFGIT_DIFF_PAGER" \ + | command awk '{ + gsub(/:/, "", $1) + print $1 + }' +} + +####################################### +# Using git grep to find word within +# all tracked files in the bare repo. +# Arguments: +# $1: the help message to display in header +# $2: the fzf delimiter to start searching, default is 3 +# $3: if exists, don't do multi select, only allow single selection +# Outputs: +# the selected file name with it's line number and line, separated by ":" +# e.g. .bash_profile:1:echo hello +####################################### +function grep_words() { + local header="${1:-Select matches to edit}" + local delimiter="${2:-3}" mydir=${0:A:h} + local grep_cmd + (( $+commands[rg] )) && \ + grep_cmd="rg --line-number --no-heading --color=auto --smart-case --ignore='.git' -- ." || \ + grep_cmd="git grep --line-number -- ." + set_fzf_multi "$2" + builtin cd -q "$FZFGIT_TREE" || exit + + eval "$grep_cmd" \ + | command fzf \ + --delimiter : \ + --nth "${delimiter}.." \ + --header="${header}" \ + --preview "$mydir/preview.sh $FZFGIT_TREE/{}" \ + | command awk -F ":" -v home="${FZFGIT_TREE}" '{ + print home "/" $1 ":" $2 + }' +} + +####################################### +# search local file +# Arguments: +# $1: string, f or d, search file or directory +# Outputs: +# A user selected file path +####################################### +function search_file() { + emulate -LR zsh + local search_type="$1" mydir=${0:A:h} + + if [[ $search_type = f ]]; then + builtin print -rl -- \ + ${${(@f)$(eval "${(@)${(@f)${${(M)${fzfgit_find:t}:#fd}:+fd . -d1 -tf}:-find . -maxdepth 1 -type f}}")}#./} \ + | command fzf --multi --preview "${mydir}/preview.sh {}" + elif [[ $search_type == d ]]; then + if (( $+commands[exa] )) { + eval "${(@)${(@f)${${(M)${fzfgit_find:t}:#fd}:+fd . -d1 -td}:-find . -maxdepth 1 -type d}}" \ + | command awk '{ if ($0 != "." && $0 != "./.git") { gsub(/^\.\//, "", $0); print $0 } }' \ + | command fzf --multi --preview "exa -TL 1 --color=always --group-directories-first --icons {}" + } elif (( $+commands[tree] )) { + eval "${(@)${(@f)${${(M)${fzfgit_find:t}:#fd}:+fd . -d1 -td}:-find . -maxdepth 1 -type d}}" \ + | command awk '{ if ($0 != "." && $0 != "./.git") { gsub(/^\.\//, "", $0); print $0 } }' \ + | command fzf --multi --preview "tree -L 1 -C --dirsfirst {}" + } else { + eval "${(@)${(@f)${${(M)${fzfgit_find:t}:#fd}:+fd . -d1 -td}:-find . -maxdepth 1 -type d}}" \ + | command awk '{ if ($0 != "." && $0 != "./.git") { gsub(/^\.\//, "", $0); print $0 } }' \ + | command fzf --multi + } + fi +} + +# vim: ft=zsh:et:sw=2:ts=2:sts=-1:fdm=marker:fmr=[[[,]]]: diff --git a/fgit_helper.sh b/fgit_helper.sh @@ -1,315 +0,0 @@ -#!/usr/bin/env bash - -####################################### -########## SETTING VARIABLES ########## -####################################### -export FZFGIT_TREE="$(git rev-parse --show-toplevel)" -export FZFGIT_DIFF_PAGER="${FZFGIT_DIFF_PAGER:-$(git config core.pager || echo 'cat')}" -export EDITOR="${EDITOR:-vim}" -export FZFGIT_VERSION="v1.2" - -[[ -z "${FZF_DEFAULT_OPTS}" ]] && export FZF_DEFAULT_OPTS='--cycle' - -if [[ -z "${FZFGIT_KEY}" ]]; then - FZFGIT_KEY=" - --bind='ctrl-a:toggle-all' - --bind='ctrl-b:execute(bat --paging=always -f {+})' - --bind='ctrl-y:execute-silent(echo {+} | pbcopy)' - --bind='ctrl-e:execute(echo {+} | xargs -o $EDITOR)' - --bind='ctrl-k:preview-up' - --bind='ctrl-j:preview-down' - --bind='alt-j:jump' - --bind='alt-0:top' - --bind='ctrl-s:toggle-sort' - --bind='?:toggle-preview' -" -fi - - -FZF_DEFAULT_OPTS=" - $FZF_DEFAULT_OPTS - --ansi - --cycle - --exit-0 - $FZFGIT_DEFAULT_OPTS - $FZFGIT_KEY -" - -[[ -z "${COLUMNS}" ]] \ - && COLUMNS=$(stty size < /dev/tty | cut -d' ' -f2) -[[ "${COLUMNS}" -lt 80 ]] \ - && FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS --preview-window=hidden" - -####################################### -# determine to set multi selection or not -# Globals: -# ${FZF_DEFAULT_OPTS}: fzf options -# Arguments: -# $1: if exists, disable multi, set single -####################################### -set_fzf_multi() { - local no_multi="$1" - if [[ -z "${no_multi}" ]]; then - export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --multi" - else - export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --no-multi" - fi -} - -####################################### -# Helper function to get user confirmation -# Globals: -# None -# Locals: -# ${confirm}: user confirmation status -# Arguments: -# $1: confirmation message to show during confirm -# Outputs: -# ${confirm}: y or n indicating user response -####################################### -get_confirmation() { - local confirm - local message="${1:-Confirm?}" - while [[ "${confirm}" != 'y' && "${confirm}" != 'n' ]]; do - read -r -p "${message}[y/n]: " confirm - done - echo "${confirm}" -} - - -####################################### -# let user select a commit interactively -# credit to forgit for the git log format -# Arguments: -# $1: the helper message to display in the fzf header -# $2: files to show diff against HEAD -# Outputs: -# the selected commit 6 char code -# e.g. b60b330 -####################################### -get_commit() { - local header="${1:-select a commit}" - local files=("${@:2}") - if [[ "${#files[@]}" -eq 0 ]]; then - git \ - log --color=always --format='%C(auto)%h%d %s %C(black)%C(bold)%cr' \ - | fzf --header="${header}" --no-multi \ - --preview "echo {} \ - | awk '{print \$1}' \ - | xargs -I __ git \ - show --color=always __ \ - | ${FZFGIT_DIFF_PAGER}" \ - | awk '{print $1}' - else - git \ - log --oneline --color=always --decorate=short \ - | fzf --header="${header}" --no-multi --preview "echo {} \ - | awk '{print \$1}' \ - | xargs -I __ git \ - diff --color=always __ ${files[*]} \ - | ${FZFGIT_DIFF_PAGER}" \ - | awk '{print $1}' - fi -} - -####################################### -# let user select a branch interactively -# Arguments: -# $1: the helper message to display in the fzf header -# Outputs: -# the selected branch name -# e.g. master -####################################### -get_branch() { - local header="${1:-select a branch}" - git branch -a \ - | awk '{ - if ($0 ~ /\*.*\(HEAD.*/) { - gsub(/\* /, "", $0) - print "\033[32m" $0 "\033[0m" - } else if (match($1, "\\*") != 0) { - print "\033[32m" $2 "\033[0m" - } else if ($0 ~ /^[ \t]+remotes\/.*/) { - gsub(/[ \t]+/, "", $0) - print "\033[31m" $0 "\033[0m" - } else { - gsub(/^[ \t]+/, "", $0) - print $0 - } - }' \ - | fzf --no-multi --header="${header}" \ - --preview="echo {} \ - | awk '{ - if (\$0 ~ /.*HEAD.*/) { - print \"HEAD\" - } else { - print \$0 - } - }' \ - | xargs -I __ git \ - log --color=always --color=always --format='%C(auto)%h%d %s %C(black)%C(bold)%cr' __" -} - -####################################### -# let user select a git tracked file interactively -# Arguments: -# $1: the helper message to display in the fzf header -# $2: print option, values (full|raw) -# $3: if exist, don't do multi selection, do single -# Outputs: -# the selected file path -# e.g.$HOME/.config/nvim/init.vim -####################################### -get_git_file() { - local mydir - local header="${1:-select tracked file}" - local print_opt="${2:-full}" - mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" - set_fzf_multi "$3" - git ls-files --full-name --directory "${FZFGIT_TREE}" \ - | fzf --header="${header}" \ - --preview "preview.sh ${FZFGIT_TREE}/{}" \ - | awk -v home="${FZFGIT_TREE}" -v print_opt="${print_opt}" '{ - if (print_opt == "full") { - print home "/" $0 - } else { - print $0 - } - }' -} - -####################################### -# let user select a modified file interactively -# Arguments: -# $1: the helper message to display in the fzf header -# $2: display mode of modified files. -# default: true -# all: display all modified, include staged and unstaged -# staged: display only staged files -# unstaged: display only unstaged files -# $3: output_format -# default: name -# name: formatted name of the file -# raw: raw file name with status -# $4: if exists, don't do multi selection, do single -# Outputs: -# the selected file path -# e.g.$HOME/.config/nvim/init.vim -####################################### - -get_modified_file() { - local header="${1:-select a modified file}" - local display_mode="${2:-all}" - local output_format="${3:-name}" - set_fzf_multi "$4" - git status --porcelain \ - | awk -v display_mode="${display_mode}" '{ - if ($0 ~ /^[[[:alpha:]]{2}.*$/) { - print "\033[32m" substr($0, 1, 1) "\033[31m" substr($0, 2) "\033[0m" - } else if ($0 ~ /^[A-Za-z][ \t].*$/) { - if (display_mode == "all" || display_mode == "staged") { - print "\033[32m" $0 "\033[0m" - } - } else { - if (display_mode == "all" || display_mode == "unstaged") { - print "\033[31m" $0 "\033[0m" - } - } - }' \ - | fzf --header="${header}" --preview "echo {} \ - | awk '{sub(\$1 FS,\"\"); print \$0}' \ - | xargs -I __ git \ - diff HEAD --color=always -- ${FZFGIT_TREE}/__ \ - | ${FZFGIT_DIFF_PAGER}" \ - | awk -v home="${FZFGIT_TREE}" -v format="${output_format}" '{ - if (format == "name") { - $1="" - gsub(/^[ \t]/, "", $0) - gsub(/"/, "", $0) - print home "/" $0 - } else { - print $0 - } - }' -} - -####################################### -# let user select a stash interactively -# Arguments: -# $1: the help message to display in header -# $2: if exists, don't do multi select, only allow single selection -# Outputs: -# the selected stash identifier -# e.g. stash@{0} -####################################### -get_stash() { - local header="${1:-select a stash}" - set_fzf_multi "$2" - git \ - stash list \ - | fzf --header="${header}" --preview "echo {} \ - | awk '{ - gsub(/:/, \"\", \$1) - print \$1 - }' \ - | xargs -I __ git \ - stash show -p __ --color=always \ - | ${FZFGIT_DIFF_PAGER}" \ - | awk '{ - gsub(/:/, "", $1) - print $1 - }' -} - -####################################### -# Using git grep to find word within -# all tracked files in the bare repo. -# Arguments: -# $1: the help message to display in header -# $2: the fzf delimiter to start searching, default is 3 -# $3: if exists, don't do multi select, only allow single selection -# Outputs: -# the selected file name with it's line number and line, separated by ":" -# e.g. .bash_profile:1:echo hello -####################################### -grep_words() { - local header="${1:-select matches to edit}" - local delimiter="${2:-3}" - local grep_cmd - command -v rg >/dev/null && grep_cmd="rg --line-number --no-heading --color=auto --smart-case --ignore='.git' -- ." \ - || grep_cmd="git grep --line-number -- ." - mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" - set_fzf_multi "$2" - cd "${FZFGIT_TREE}" || exit - eval "${grep_cmd}" \ - | fzf --delimiter : --nth "${delimiter}".. --header="${header}" \ - --preview "${mydir}/preview.sh ${FZFGIT_TREE}/{}" \ - | awk -F ":" -v home="${FZFGIT_TREE}" '{ - print home "/" $1 ":" $2 - }' -} - -####################################### -# search local file -# Arguments: -# $1: string, f or d, search file or directory -# Outputs: -# A user selected file path -####################################### -search_file() { - local search_type="$1" mydir - mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" - if [[ "${search_type}" == "f" ]]; then - find . -maxdepth 1 -type f | sed "s|\./||g" | fzf --multi --preview "${mydir}/preview.sh {}" - elif [[ "${search_type}" == "d" ]]; then - if command -v exa &>/dev/null; then - find . -maxdepth 1 -type d | awk '{if ($0 != "." && $0 != "./.git"){gsub(/^\.\//, "", $0);print $0}}' \ - | fzf --multi --preview "exa -TL 1 --color=always --group-directories-first --icons {}" - elif command -v tree &>/dev/null; then - find . -maxdepth 1 -type d | awk '{if ($0 != "." && $0 != "./.git"){gsub(/^\.\//, "", $0);print $0}}' \ - | fzf --multi --preview "tree -L 1 -C --dirsfirst {}" - else - find . -maxdepth 1 -type d | awk '{if ($0 != "." && $0 != "./.git"){gsub(/^\.\//, "", $0);print $0}}' | fzf --multi - fi - fi -} diff --git a/flog b/flog @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: git log interactive viewer # # @params @@ -16,26 +19,29 @@ # -c|--checkout: checkout selected commmit # -y|--yes: confirm action by default and skip confirmation -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: flog [-h] [-r] [-R] [-e] [-c] [-y] ... +function usage() { + builtin print -P -- "\ +%F{1}%BUsage%b%f: + %F{2}flog%f [%F{13}-h%f] [%F{13}-r%f] [%F{13}-R%f] [%F{13}-e%f] [%F{13}-c%f] [%F{13}-y%f] ... Interactive log viewer with action menu. Action menu contains options including revert|reset|edit|checkout|exit. -Default: list all commits and prompt a menu to select action to perform. +%F{4}Default%f: list all commits and prompt a menu to select action to perform. -Optional arguments: - -h, --help\t\tshow this help message and exit. - -r, --revert\t\trevert the selected commit and skip action menu. - -R, --reset\t\treset HEAD back to selected commit and skip action menu. - -e, --edit\t\tedit selected commit through interactive rebase and skip action menu. - -c, --checkout\tcheckout selected commit and skip action menu. - -y, --yes\t\tacknowledge all actions that will be taken and skip confirmation." +%F{1}%BOptions%f%b: + %F{13}-h%f, %F{13}--help%f Show this help message and exit + %F{13}-r%f, %F{13}--revert%f Revert the selected commit and skip action menu + %F{13}-R%f, %F{13}--reset%f Reset HEAD back to selected commit and skip action menu + %F{13}-e%f, %F{13}--edit%f Edit selected commit through interactive rebase and skip action menu + %F{13}-c%f, %F{13}--checkout%f Checkout selected commit and skip action menu + %F{13}-y%f, %F{13}--yes%f Acknowledge all actions that will be taken and skip confirmation" } ####################################### @@ -46,71 +52,69 @@ Optional arguments: # Outputs: # ${selected_action}: user selected action ####################################### -draw_menu() { +function draw_menu() { local selected_commit="$1" local selected_action="$2" local menu header message - if [[ -n "$selected_action" ]]; then - echo "${selected_action}" - else + [[ -n "$selected_action" ]] && { + builtin print -r -- "$selected_action" + } || { menu="revert: revert the selected commit\n" menu="${menu}reset: reset HEAD to the selected commit using --mixed flag\n" menu="${menu}edit: edit selected commit through interactive rebase\n" menu="${menu}checkout: checkout the selected commit\n" menu="${menu}exit: quit flog" message=$( - git \ - log --format=%B -n 1 "${selected_commit}" + command git log \ + --format=%B -n 1 "$selected_commit" ) - header="commit ${selected_commit}: ${message}" - selected_action=$(echo -e "${menu}" \ - | fzf --no-multi --header="${header}" \ - | awk -F ":" '{ + header="commit $selected_commit: $message" + selected_action=$(print -- "$menu" \ + | fzf --no-multi --header="$header" \ + | command awk -F ":" '{ print $1 }' ) - echo "${selected_action}" - fi + builtin print -r -- "$selected_action" + } } -selected_action="" -selected_commit="" -confirm="" - -while [[ "$#" -gt 0 ]]; do +while (( $# )) { case "$1" in - -r|--revert) selected_action="revert"; shift ;; - -R|--reset) selected_action="reset"; shift ;; - -e|--edit) selected_action="edit"; shift ;; - -c|--checkout) selected_action="checkout"; shift ;; - -y|--yes) confirm='y'; shift ;; - -h|--help) usage && exit 0 ;; - *) echo "Invalid option: $1" >&2 && usage && exit 1 ;; + (-r|--revert) selected_action="revert"; shift ;; + (-R|--reset) selected_action="reset"; shift ;; + (-e|--edit) selected_action="edit"; shift ;; + (-c|--checkout) selected_action="checkout"; shift ;; + (-y|--yes) confirm='y'; shift ;; + (-h|--help) usage && exit 0 ;; + (*) print::error "Invalid option: $1" && { usage; exit 1 } ;; esac -done +} while :; do selected_commit=$(get_commit) - [[ -z "${selected_commit}" ]] && exit 1 + [[ -z "$selected_commit" ]] && exit 1 selected_action=$(draw_menu "${selected_commit}" "${selected_action}") - [[ -n "${selected_action}" ]] && break + [[ -n "$selected_action" ]] && break done if [[ "${selected_action}" != 'exit' ]]; then if [[ "${selected_action}" == "reset" ]] && [[ -z "${confirm}" ]]; then - echo "(dryrun) reset HEAD to ${selected_commit}" - elif [[ -z "${confirm}" ]]; then - echo "(dryrun) ${selected_action} ${selected_commit}" + print -P -- "(%F{3}dryrun%f) reset HEAD to $selected_commit" + elif [[ -z "$confirm" ]]; then + print -P -- "(%F{3}dryrun%f) ${selected_action} ${selected_commit}" fi - [[ -z "${confirm}" ]] && confirm=$(get_confirmation) - [[ "${confirm}" != 'y' ]] && exit 1 + + [[ -z "$confirm" ]] && confirm=$(get_confirmation) + [[ "$confirm" != y ]] && exit 1 + notify-send 'past' fi -case "${selected_action}" in - revert) git revert "${selected_commit}" ;; - reset) git reset "${selected_commit}" ;; - edit) git rebase -i "${selected_commit}"~ ;; - checkout) git checkout "${selected_commit}" ;; - exit) exit 0 ;; - *) exit 1 ;; -esac +case "$selected_action" { + (revert) git revert "${selected_commit}" ;; + (reset) git reset "${selected_commit}" ;; + (edit) git rebase -i "${selected_commit}"~ ;; + (checkout) git checkout "${selected_commit}" ;; + (exit) exit 0 ;; + (*) exit 1 ;; +} diff --git a/freset b/freset @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: unstage selected staged file || reset the commit to certain point # # @params @@ -17,54 +20,55 @@ # -H|--hard: use --hard flag # -y|--yes: confirm action by default and skip confirmation -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: freset [-h] [-c] [-S] [-H] [-y] ... +function usage() { + builtin print -P -- "\ +%F{1}%BUsage%f%b: %F{2}freset%f [%F{13}-h%f] [%F{13}-c%f] [%F{13}-S%f] [%F{13}-H%f] [%F{13}-y%f] ... Reset(unstage) the selected staged files. Reset the HEAD to certain commits by using -c flag. -Default: unstage the selected files. +%F{4}Default%f: unstage the selected files. -Optional arguments: - -h, --help\t\tshow this help message and exit. - -c, --commit\t\treset HEAD to certain commit, default --mixed flag, reset HEAD to certain commit put all changes into modified state. - -S, --soft\t\treset commit using --soft flag, reset HEAD to certain commit without modify working tree. - -H, --hard\t\treset commit using --hard flag, reset HEAD to certain commit discard all changes from the working tree. - -y, --yes\t\tacknowledge all actions that will be taken and skip confirmation." +%F{1}%BOptions%f%b: + %F{13}-h%f, %F{13}--help%f Show this help message and exit + %F{13}-c%f, %F{13}--commit%f Reset HEAD to certain commit + %F{4}Default%f: --mixed flag, reset HEAD to certain commit put all changes into modified state + %F{13}-S%f, %F{13}--soft%f Reset commit using --soft flag, reset HEAD to certain commit without modify working tree + %F{13}-H%f, %F{13}--hard%f Reset commit using --hard flag, reset HEAD to certain commit discard all changes from the working tree + %F{13}-y%f, %F{13}--yes%f Acknowledge all actions that will be taken and skip confirmation" } +local reset_option reset_type +local -a selected_files reset_option="--mixed" reset_type="modified" -typeset -a selected_files && selected_files=() -confirm="" -selected_commit="" -while [[ "$#" -gt 0 ]]; do +while (( $# )) { case "$1" in - -c|--commit) reset_type="commit"; shift ;; - -S|--soft) reset_option="--soft"; shift ;; - -H|--hard) reset_option="--hard"; shift ;; - -y|--yes) confirm='y'; shift ;; - -h|--help) usage && exit 0 ;; - *) echo "Invalid option: $1" >&2 && usage && exit 1 ;; + (-c|--commit) reset_type="commit"; shift ;; + (-S|--soft) reset_option="--soft"; shift ;; + (-H|--hard) reset_option="--hard"; shift ;; + (-y|--yes) confirm='y'; shift ;; + (-h|--help) usage; exit 0 ;; + (*) print::error "Invalid option: $1" && { usage; exit 1 } ;; esac -done +} -if [[ "${reset_type}" == "commit" ]]; then - selected_commit=$(get_commit "select the target commit") - [[ -z "${selected_commit}" ]] && exit 1 - [[ -z "${confirm}" ]] && confirm=$(get_confirmation "Reset HEAD to ${selected_commit} ${reset_option}?") - [[ "${confirm}" != 'y' ]] && exit 1 - git reset "${selected_commit}" "${reset_option}" +if [[ "$reset_type" = "commit" ]]; then + selected_commit=$(get_commit "Select the target commit") + [[ -z "$selected_commit" ]] && exit 1 + [[ -z "$confirm" ]] && confirm=$(get_confirmation "Reset HEAD to $selected_commit $reset_option?") + [[ "$confirm" != y ]] && exit 1 + command git reset "$selected_commit" "$reset_option" else - while IFS= read -r line; do - selected_files+=("${line}") - done < <(get_modified_file 'select files to unstage' 'staged') - [[ "${#selected_files[@]}" -eq 0 ]] && exit 1 - git reset "${selected_files[@]}" + selected_files=( ${(@f)"$(get_modified_file 'select files to unstage' 'staged')"} ) + (( $#selected_files )) || exit 1 + command git reset "${selected_files[@]}" + fi diff --git a/fstash b/fstash @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: stash operation using fzf # # @params @@ -15,64 +18,67 @@ # -d|--delete: delete selected stash # -p|--pop: use pop instead of apply on the selected stash -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: fstash [-h] [-s] [-d] [-p] ... +function usage() { + builtin print -P -- "\ +%F{1}%BUsage%f%b: %F{2}fstash%f [%F{13}-h%f] [%F{13}-s%f] [%F{13}-d%f] [%F{13}-p%f] ... View and manage stash interactively. -Default: list all stashes and apply the selected stash. +%F{4}Default%f: list all stashes and apply the selected stash. -Optional arguments: - -h, --help\t\tshow this help message and exit. - -s, --select\t\tlist modified files and stash the selected files. - -d, --delete\t\tlist all stashes and delete the selected stash from stash list. - -p, --pop\t\tuse 'stash pop' instead of 'stash apply'." +%F{1}%BOptions%f%b: + %F{13}-h%f, %F{13}--help%f Show this help message and exit + %F{13}-s%f, %F{13}--select%f List modified files and stash the selected files + %F{13}-d%f, %F{13}--delete%f List all stashes and delete the selected stash from stash list + %F{13}-p%f, %F{13}--pop%f Use 'stash pop' instead of 'stash apply'" } +local stash_command confirm +local -a selected_files selected_stash stash_command="apply" -typeset -a selected_files && selected_files=() -selected_stash="" -confirm="" -while [[ "$#" -gt 0 ]]; do +while (( $# )) { case "$1" in - -p|--pop) stash_command="pop"; shift ;; - -s|--select) stash_command="select"; shift ;; - -d|--delete) stash_command="delete"; shift ;; - -y|--yes) confirm="y"; shift ;; - -h|--help) usage && exit 0 ;; - *) echo "Invalid option: $1" >&2 && usage && exit 1 ;; + (-p|--pop) stash_command="pop"; shift ;; + (-s|--select) stash_command="select"; shift ;; + (-d|--delete) stash_command="delete"; shift ;; + (-y|--yes) confirm="y"; shift ;; + (-h|--help) usage; exit 0 ;; + (*) print::error "Invalid option: $1" && { usage; exit 1 } ;; esac -done +} + +local line + +if [[ "$stash_command" == "select" ]]; then + selected_files=( ${(@f)"$(get_modified_file "select files to add to a stash")"} ) + (( $#selected_files )) || exit 1 + command git stash -- "$selected_files[@]" +elif [[ "$stash_command" == "delete" ]]; then + selected_stash=( ${(@f)"$(get_stash 'select stash to delete')"} ) + (( $#selected_stash )) || exit 1 + [[ -z "$confirm" ]] && \ + foreach line ("$selected_stash[@]") { + print -P -- "(%F{3}dryrun%f) drop ${line}" + } + + [[ -z "$confirm" ]] && confirm=$(get_confirmation) + [[ "$confirm" != y ]] && exit 1 -if [[ "${stash_command}" == "select" ]]; then - while IFS= read -r line; do - selected_files+=("${line}") - done < <(get_modified_file "select files to add to a stash") - [[ "${#selected_files[@]}" -eq 0 ]] && exit 1 - git stash -- "${selected_files[@]}" -elif [[ "${stash_command}" == "delete" ]]; then - selected_stash=$(get_stash "select stash to delete") - [[ -z "${selected_stash}" ]] && exit 1 - [[ -z "${confirm}" ]] && \ - while IFS= read -r line; do - echo "(dryrun) drop ${line}" - done <<< "${selected_stash}" - [[ -z "${confirm}" ]] && confirm=$(get_confirmation) - [[ "${confirm}" != 'y' ]] && exit 1 - while IFS= read -r line; do - git stash drop "${line}" - done <<< "${selected_stash}" + foreach line ("$selected_stash[@]") { + command git stash drop "${line}" + } else - selected_stash=$(get_stash "select stash to apply" "true") - [[ -z "${selected_stash}" ]] && exit 1 - [[ -z "${confirm}" ]] && echo "(dryrun) ${stash_command} ${selected_stash}" - [[ -z "${confirm}" ]] && confirm=$(get_confirmation) - [[ "${confirm}" != 'y' ]] && exit 1 - git stash "${stash_command}" "${selected_stash}" + selected_stash=( ${(@f)"$(get_stash 'select stash to apply' 'true')"} ) + (( $#selected_stash )) || exit 1 + [[ -z "$confirm" ]] && print -P -- "(%F{3}dryrun%f) $stash_command $selected_stash" + [[ -z "$confirm" ]] && confirm=$(get_confirmation) + [[ "$confirm" != y ]] && exit 1 + command git stash "$stash_command" "$selected_stash" fi diff --git a/fstat b/fstat @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: interactive git status menu; toggle stage and unstage # # @params @@ -9,20 +12,21 @@ # ${selected_filenames}: bash array of names for the selected_files # ${stage_file}: determine if current operation should be staging file or unstage -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -# mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: fstat [-h] ... +function usage() { + builtin print -P -- "\ +%F{1}%BUsage%f%b: %F{2}fstat%f [%F{13}-h%f] ... Display interactive git status menu. Toggle file stage/unstage interactively. -Optional arguments: - -h, --help\t\tshow this help message and exit." +%F{1}%BOptions%f%f: + %F{13}-h%f, %F{13}--help%f Show this help message and exit" } while [[ "$#" -gt 0 ]]; do diff --git a/funtrack b/funtrack @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: untrack selected files # # @params @@ -9,30 +12,32 @@ # ${confirm}: user confirm status # ${selected_files}: arrays of user selected_files for operation -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops -mydir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" -source "${mydir}"/fgit_helper.sh +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -usage() { - echo -e "Usage: funtrack [-h] [-t] [-r] [-y] ... +function usage() { + builtin print -P -- "\ +%F{1}%BUsage%f%b: %F{2}funtrack%f [%F{13}-h%f] [%F{13}-t%f] [%F{13}-r%f] [%F{13}-y%f] ... Untrack selected files from git. -Default: list all tracked files and permanently untrack the selected files (using git rm --cached filename). +%F{4}Default%f: list all tracked files and permanently untrack the selected files (using git rm --cached filename). Files will be remove from index while keeping the file in your current system. However, when your other computers pull down the changes, the untracked files will be deleted. Make sure to run fbackup before pulling down the changes. -Alternatively use the -t flag (using git update-index --assume-unchanged [path]) to temporarily +Alternatively use the %F{13}-t%f flag (using git update-index --assume-unchanged [path]) to temporarily untrack a file but keeping the files when other computers pull down the changes. -Optional arguments: - -h, --help\t\tshow this help message and exit. - -t, --temp\t\tlist all tracked files and temporarily ignore changes of the selected files. - -r, --resume\t\tlist all tracked files and resume tracking changes of the selected files. - -y, --yes\t\tacknowledge all actions that will be taken and skip confirmation." +%F{1}%BOptions%f%b: + %F{13}-h%f, %F{13}--help%f Show this help message and exit + %F{13}-t%f, %F{13}--temp%f List all tracked files and temporarily ignore changes of the selected files + %F{13}-r%f, %F{13}--resume%f List all tracked files and resume tracking changes of the selected files + %F{13}-y%f, %F{13}--yes%f Acknowledge all actions that will be taken and skip confirmation" } track_type="untrack" diff --git a/fzgrep b/fzgrep @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Grep for words within all dotfiles # # @params @@ -8,10 +11,12 @@ # selected_lines: selected lines to edit # fzf_search_delimiter: the col to start searching in fzf -set -ef +emulate -LR zsh +setopt warncreateglobal noshortloops + +local mydir; mydir=${0:h:A} +builtin source ${mydir}/fgit_helper -mydir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -source "${mydir}"/fgit_helper.sh usage() { echo -e "Usage: fgrep [-h] [-c] [-f] ... diff --git a/preview.sh b/preview.sh @@ -1,5 +1,8 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env zsh + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + # Desc: dynamic preview command; borrowed & modified from fzf.vim # can export FZFGIT_PREVIEW='bat -Pf --style auto' #