diff --git a/.config/fish/completions/update.fish b/.config/fish/completions/update.fish new file mode 100644 index 00000000..14f12027 --- /dev/null +++ b/.config/fish/completions/update.fish @@ -0,0 +1,3 @@ +complete -c update -s h -l help -d 'Print help' +complete -c update -s y -l no-confirm -d 'No confirmation' +complete -c update -s o -l one-term -d 'Force single terminal' diff --git a/.config/fish/conf.d/bin/bin/git-forgit b/.config/fish/conf.d/bin/bin/git-forgit new file mode 100755 index 00000000..27a893d1 --- /dev/null +++ b/.config/fish/conf.d/bin/bin/git-forgit @@ -0,0 +1,1211 @@ +#!/usr/bin/env bash +# MIT (c) Wenxuan Zhang + +# This file is meant to be executed directly. If it's available on the PATH, +# it can also be used as a subcommand of git, which then forwards all arguments +# on to forgit. So, all of these commands will work as expected: +# +# `git forgit log` +# `git forgit checkout_file` +# `git forgit checkout_file README.md` +# +# This gives users the choice to set aliases inside of their git config instead +# of their shell config if they prefer. + +# Check if fzf is installed +installed_fzf_version=$(fzf --version 2>/dev/null | awk '{print $1}') +if [[ -z "$installed_fzf_version" ]]; then + echo "fzf is not installed. Please install fzf first." + exit 1 +fi + +# Check fzf version +required_fzf_version="0.49.0" +higher_fzf_version=$(printf '%s\n' "$required_fzf_version" "$installed_fzf_version" | sort -V | tail -n1) +if [[ "$higher_fzf_version" != "$installed_fzf_version" ]]; then + echo "fzf version $required_fzf_version or higher is required. You have $installed_fzf_version." + exit 1 +fi + +# Set shell for fzf preview commands +# Disable shellcheck for "which", because it suggests "command -v xxx" instead, +# which is not a working replacement. +# See https://github.com/koalaman/shellcheck/issues/1162 +# shellcheck disable=2230 +SHELL="$(which bash)" +export SHELL + +# Get absolute forgit path +FORGIT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)/$(basename -- "${BASH_SOURCE[0]}") + +FORGIT_FZF_DEFAULT_OPTS=" +$FZF_DEFAULT_OPTS +--ansi +--height='80%' +--bind='alt-k:preview-up,alt-p:preview-up' +--bind='alt-j:preview-down,alt-n:preview-down' +--bind='ctrl-r:toggle-all' +--bind='ctrl-s:toggle-sort' +--bind='?:toggle-preview' +--bind='alt-w:toggle-preview-wrap' +--preview-window='right:60%' ++1 +$FORGIT_FZF_DEFAULT_OPTS +" + +_forgit_warn() { printf "%b[Warn]%b %s\n" '\e[0;33m' '\e[0m' "$@" >&2; } +_forgit_info() { printf "%b[Info]%b %s\n" '\e[0;32m' '\e[0m' "$@" >&2; } +_forgit_inside_work_tree() { git rev-parse --is-inside-work-tree >/dev/null; } +# tac is not available on OSX, tail -r is not available on Linux, so we use either of them +_forgit_reverse_lines() { tac 2> /dev/null || tail -r; } + +_forgit_previous_commit() { + # "SHA~" is invalid when the commit is the first commit, but we can use "--root" instead + if [[ "$(git rev-parse "$1")" == "$(git rev-list --max-parents=0 HEAD)" ]]; then + echo "--root" + else + echo "$1~" + fi +} + +_forgit_contains_non_flags() { + while (("$#")); do + case "$1" in + -*) shift ;; + *) + return 0 + ;; + esac + done + return 1 +} + +# optional render emoji characters (https://github.com/wfxr/emoji-cli) +_forgit_emojify() { + if hash emojify &>/dev/null; then + emojify + else + cat + fi +} + +# extract the first git sha occurring in the input and strip trailing newline +_forgit_extract_sha() { + grep -Eo '[a-f0-9]+' | head -1 | tr -d '[:space:]' +} + +# extract the first git sha and copy it to the clipboard +_forgit_yank_sha() { + echo "$1" | _forgit_extract_sha | ${FORGIT_COPY_CMD:-pbcopy} +} + +# extract the first stash name in the input +_forgit_extract_stash_name() { + cut -d: -f1 | tr -d '[:space:]' +} + +# extract the first stash name and copy it to the clipboard +_forgit_yank_stash_name() { + echo "$1" | _forgit_extract_stash_name | ${FORGIT_COPY_CMD:-pbcopy} +} + +# parse a space separated string into an array +# arrays parsed with this function are global +_forgit_parse_array() { + ${IFS+"false"} && unset old_IFS || old_IFS="$IFS" + # read the value of the second argument + # into an array that has the name of the first argument + IFS=" " read -r -a "$1" <<< "$2" + ${old_IFS+"false"} && unset IFS || IFS="$old_IFS" +} + +# parse the input arguments and print only those after the "--" +# separator as a single line of quoted arguments to stdout +_forgit_quote_files() { + local files add + files=() + add=false + while (( "$#" )); do + case "$1" in + --) + add=true + shift + ;; + *) + if [ $add == true ]; then + files+=("'$1'") + fi + shift + ;; + esac + done + echo "${files[*]}" +} + +_forgit_log_graph_enable=${FORGIT_LOG_GRAPH_ENABLE:-"true"} +_forgit_log_format=${FORGIT_LOG_FORMAT:-%C(auto)%h%d %s %C(black)%C(bold)%cr%Creset} +_forgit_log_preview_options=("--graph" "--pretty=format:$_forgit_log_format" "--color=always" "--abbrev-commit" "--date=relative") +_forgit_fullscreen_context=${FORGIT_FULLSCREEN_CONTEXT:-10} +_forgit_preview_context=${FORGIT_PREVIEW_CONTEXT:-3} +_forgit_dir_view=${FORGIT_DIR_VIEW:-$(hash tree &> /dev/null && echo 'tree' || echo 'find')} + +_forgit_pager() { + local pager + pager=$(_forgit_get_pager "$1") + [[ -z "${pager}" ]] && exit 1 + eval "${pager} ${*:2}" +} + +_forgit_get_pager() { + local pager + pager=${1:-core} + case "$pager" in + core) echo -n "${FORGIT_PAGER:-$(git config core.pager || echo 'cat')}" ;; + show) echo -n "${FORGIT_SHOW_PAGER:-$(git config pager.show || _forgit_get_pager)}" ;; + diff) echo -n "${FORGIT_DIFF_PAGER:-$(git config pager.diff || _forgit_get_pager)}" ;; + ignore) echo -n "${FORGIT_IGNORE_PAGER:-$(hash bat &>/dev/null && echo 'bat -l gitignore --color=always' || echo 'cat')}" ;; + attributes) echo -n "${FORGIT_ATTRIBUTES_PAGER:-$(hash bat &>/dev/null && echo 'bat -l gitattributes --color=always' || echo 'cat')}" ;; + blame) echo -n "${FORGIT_BLAME_PAGER:-$(git config pager.blame || _forgit_get_pager)}" ;; + enter) echo -n "${FORGIT_ENTER_PAGER:-"LESS='-r' less"}" ;; + *) echo "pager not found: $1" >&2 ;; + esac +} + +_forgit_is_file_tracked() { + git ls-files "$1" --error-unmatch &> /dev/null +} + +_forgit_list_files() { + local rootdir + rootdir=$(git rev-parse --show-toplevel) + # git escapes special characters in it's output when core.quotePath is + # true or unset. Git always expects unquoted file paths as input. This + # leads to issues when we consume output from git and use it to build + # input for other git commands. Use the -z flag to ensure file paths are + # unquoted. + # uniq is necessary because unmerged files are printed once for each + # merge conflict. + # With the -z flag, git also uses \0 line termination, so we + # have to replace the terminators. + git ls-files -z "$@" "$rootdir" | tr '\0' '\n' | uniq +} + +_forgit_log_preview() { + local sha + sha=$(echo "$1" | _forgit_extract_sha) + shift + echo "$sha" | xargs -I% git show --color=always -U"$_forgit_preview_context" % -- "$@" | _forgit_pager show +} + +_forgit_log_enter() { + local sha + sha=$(echo "$1" | _forgit_extract_sha) + shift + echo "$sha" | xargs -I% "${FORGIT}" show % "$@" +} + +# git commit viewer +_forgit_log() { + _forgit_inside_work_tree || return 1 + local opts graph quoted_files log_format + quoted_files=$(_forgit_quote_files "$@") + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index + --bind=\"enter:execute($FORGIT log_enter {} $quoted_files)\" + --bind=\"ctrl-y:execute-silent($FORGIT yank_sha {})\" + --preview=\"$FORGIT log_preview {} $quoted_files\" + $FORGIT_LOG_FZF_OPTS + " + graph=() + [[ $_forgit_log_graph_enable == true ]] && graph=(--graph) + log_format=${FORGIT_GLO_FORMAT:-$_forgit_log_format} + _forgit_log_git_opts=() + _forgit_parse_array _forgit_log_git_opts "$FORGIT_LOG_GIT_OPTS" + git log "${graph[@]}" --color=always --format="$log_format" "${_forgit_log_git_opts[@]}" "$@" | + _forgit_emojify | + FZF_DEFAULT_OPTS="$opts" fzf + fzf_exit_code=$? + # exit successfully on 130 (ctrl-c/esc) + [[ $fzf_exit_code == 130 ]] && return 0 + return $fzf_exit_code +} + +# git reflog viewer +_forgit_reflog() { + _forgit_inside_work_tree || return 1 + _forgit_contains_non_flags "$@" && { git reflog "$@"; return $?; } + local opts reflog_format + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index + --bind=\"enter:execute($FORGIT log_enter {})\" + --bind=\"ctrl-y:execute-silent($FORGIT yank_sha {})\" + --preview=\"$FORGIT log_preview {}\" + $FORGIT_REFLOG_FZF_OPTS + " + reflog_format=${FORGIT_GRL_FORMAT:-$_forgit_log_format} + _forgit_reflog_git_opts=() + _forgit_parse_array _forgit_reflog_git_opts "$FORGIT_REFLOG_GIT_OPTS" + git reflog show --color=always --format="$reflog_format" "${_forgit_reflog_git_opts[@]}" "$@" | + _forgit_emojify | + FZF_DEFAULT_OPTS="$opts" fzf + fzf_exit_code=$? + # exit successfully on 130 (ctrl-c/esc) + [[ $fzf_exit_code == 130 ]] && return 0 + return $fzf_exit_code +} + +_forgit_get_files_from_diff_line() { + # Construct a null-terminated list of the filenames + # The input looks like one of these lines: + # [R100] file -> another file + # [A] file with spaces + # [D] oldfile + # And we transform it to this representation for further usage with "xargs -0": + # file\0another file\0 + # file with spaces\0 + # oldfile\0 + # We have to do a two-step sed -> tr pipe because OSX's sed implementation does + # not support the null-character directly. + sed 's/^[[:space:]]*\[[A-Z0-9]*\][[:space:]]*//' | sed 's/ -> /\n/' | tr '\n' '\0' +} + +_forgit_get_single_file_from_diff_line() { + # Similar to the function above, but only gets a single file from a single line + # Gets the new name of renamed files + sed 's/^[[:space:]]*\[[A-Z0-9]*\][[:space:]]*//' | sed 's/.*-> //' +} + +_forgit_exec_diff() { + _forgit_diff_git_opts=() + _forgit_parse_array _forgit_diff_git_opts "$FORGIT_DIFF_GIT_OPTS" + git diff --color=always "${_forgit_diff_git_opts[@]}" "$@" +} + +_forgit_diff_view() { + local input_line=$1 + local diff_context=$2 + local repo + local commits=() + repo=$(git rev-parse --show-toplevel) + cd "$repo" || return 1 + if [ $# -gt 2 ]; then + IFS=" " read -r -a commits <<< "${*:3}" + fi + echo "$input_line" | _forgit_get_files_from_diff_line | xargs -0 \ + "$FORGIT" exec_diff "${commits[@]}" -U"$diff_context" -- | _forgit_pager diff +} + +_forgit_edit_diffed_file() { + local input_line rootdir + input_line=$1 + rootdir=$(git rev-parse --show-toplevel) + filename=$(echo "$input_line" | _forgit_get_single_file_from_diff_line) + $EDITOR "$rootdir/$filename" >/dev/tty /dev/null ; then + if [[ $# -gt 1 ]] && git rev-parse "$2" -- &>/dev/null; then + commits=("$1" "$2") && files=("${@:3}") + else + commits=("$1") && files=("${@:2}") + fi + else + files=("$@") + fi + } + # Git stashes are named "stash@{x}", which contains the fzf placeholder "{x}". + # In order to support passing stashes as arguments to _forgit_diff, we have to + # prevent fzf from interpreting this substring by escaping the opening bracket. + # The string is evaluated a few subsequent times, so we need multiple escapes. + for commit in "${commits[@]}"; do + escaped_commits+="'${commit//\{/\\\\\{}' " + done + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +m -0 --bind=\"enter:execute($FORGIT diff_enter {} $escaped_commits | $FORGIT pager enter)\" + --preview=\"$FORGIT diff_view {} '$_forgit_preview_context' $escaped_commits\" + --bind=\"alt-e:execute($FORGIT edit_diffed_file {})+refresh-preview\" + $FORGIT_DIFF_FZF_OPTS + --prompt=\"${commits[*]} > \" + " + _forgit_diff_git_opts=() + _forgit_parse_array _forgit_diff_git_opts "$FORGIT_DIFF_GIT_OPTS" + git diff --name-status "${_forgit_diff_git_opts[@]}" "${commits[@]}" -- "${files[@]}" | + sed -E 's/^([[:alnum:]]+)[[:space:]]+(.*)$/[\1] \2/' | + sed 's/ / -> /2' | expand -t 8 | + FZF_DEFAULT_OPTS="$opts" fzf + fzf_exit_code=$? + # exit successfully on 130 (ctrl-c/esc) + [[ $fzf_exit_code == 130 ]] && return 0 + return $fzf_exit_code +} + +_forgit_exec_show() { + _forgit_show_git_opts=() + _forgit_parse_array _forgit_show_git_opts "$FORGIT_SHOW_GIT_OPTS" + git show --pretty="" --diff-merges=first-parent --color=always "${_forgit_show_git_opts[@]}" "$@" +} + +_forgit_show_view() { + local input_line=$1 + local diff_context=$2 + local commit=$3 + local repo + repo=$(git rev-parse --show-toplevel) + cd "$repo" || return 1 + echo "$input_line" | _forgit_get_files_from_diff_line | xargs -0 \ + "$FORGIT" exec_show "${commit}^{commit}" -U"$diff_context" -- | _forgit_pager diff +} + +_forgit_show_preview() { + local input_line=$1 + local diff_context=$2 + local commit=$3 + if [[ "$FZF_PREVIEW_LABEL" =~ "Diff" ]]; then + _forgit_show_view "${input_line}" "${diff_context}" "${commit}" + else + git show --quiet --color=always "${FZF_PROMPT%% *}" + fi +} + +_forgit_show_enter() { + file=$1 + commit=$2 + _forgit_show_view "$file" "$_forgit_fullscreen_context" "${commit}" +} + +# git show viewer +_forgit_show() { + _forgit_inside_work_tree || return 1 + local files opts commit escaped_commit + files=() + if [[ $# -ne 0 ]]; then + if git rev-parse "$1" -- &>/dev/null ; then + commit="$1" && files=("${@:2}") + else + commit="HEAD" && files=("$@") + fi + else + commit="HEAD" + fi + # Escape opening brackets to support stashes (see comment in _forgit_diff) + escaped_commit=${commit//\{/\\\\\{} + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +m -0 --bind=\"enter:execute($FORGIT show_enter {} $escaped_commit | $FORGIT pager enter)\" + --preview=\"$FORGIT show_preview {} '$_forgit_preview_context' $escaped_commit\" + --preview-label=\" Diff \" + --bind=\"alt-e:execute($FORGIT edit_diffed_file {})+refresh-preview\" + --bind=\"alt-t:transform:[[ ! \\\"\$FZF_PREVIEW_LABEL\\\" =~ 'Diff' ]] && + echo 'change-preview-label( Diff )+refresh-preview' || + echo 'change-preview-label( Commit Message )+refresh-preview'\" + $FORGIT_DIFF_FZF_OPTS + --prompt=\"${commit} > \" + " + _forgit_show_git_opts=() + _forgit_parse_array _forgit_show_git_opts "$FORGIT_SHOW_GIT_OPTS" + # Add "^{commit}" suffix after the actual commit. This suppresses the tag information in case it is a tag. + # See: https://git-scm.com/docs/git-show#Documentation/git-show.txt-codegitshow-s--formatsv100commitcode + git show --pretty="" --name-status --diff-merges=first-parent "${_forgit_show_git_opts[@]}" "${commit}^{commit}" \ + -- "${files[@]}" | + sed -E 's/^([[:alnum:]]+)[[:space:]]+(.*)$/[\1] \2/' | + sed 's/ / -> /2' | expand -t 8 | + FZF_DEFAULT_OPTS="$opts" fzf + fzf_exit_code=$? + # exit successfully on 130 (ctrl-c/esc) + [[ $fzf_exit_code == 130 ]] && return 0 + return $fzf_exit_code +} + +_forgit_add_preview() { + file=$(echo "$1" | _forgit_get_single_file_from_add_line) + if (git status -s -- "$file" | grep '^??') &>/dev/null; then # diff with /dev/null for untracked files + git diff --color=always --no-index -- /dev/null "$file" | _forgit_pager diff | sed '2 s/added:/untracked:/' + else + git diff --color=always -- "$file" | _forgit_pager diff + fi +} + +_forgit_git_add() { + _forgit_add_git_opts=() + _forgit_parse_array _forgit_add_git_opts "$FORGIT_ADD_GIT_OPTS" + git add "${_forgit_add_git_opts[@]}" "$@" +} + +_forgit_get_single_file_from_add_line() { + # NOTE: paths listed by 'git status -su' mixed with quoted and unquoted style + # remove indicators | remove original path for rename case | remove surrounding quotes + sed 's/^.*] //' | + sed 's/.* -> //' | + sed -e 's/^\"//' -e 's/\"$//' +} + +_forgit_edit_add_file() { + local input_line=$1 + filename=$(echo "$input_line" | _forgit_get_single_file_from_add_line) + $EDITOR "$filename" >/dev/tty newest when you multiselect + # The instances of "cut", "nl" and "sort" all serve this purpose + # Please see https://github.com/wfxr/forgit/issues/253 for more details + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + --preview=\"$FORGIT cherry_pick_preview {}\" + --multi --ansi --with-nth 2.. -0 --tiebreak=index + $FORGIT_CHERRY_PICK_FZF_OPTS + " + # Note: do not add any pipe after the fzf call here, otherwise the fzf_exitval is not propagated properly. + # Any eventual post processing can be done afterwards when the "commits" variable is assigned below. + fzf_selection=$(git log --right-only --color=always --cherry-pick --oneline "$base"..."$target" | nl | + FZF_DEFAULT_OPTS="$opts" fzf) + fzf_exitval=$? + [[ $fzf_exitval != 0 ]] && return $fzf_exitval + [[ -z "$fzf_selection" ]] && return $fzf_exitval + + commits=() + while IFS='' read -r commit; do + commits+=("$commit") + done < <(echo "$fzf_selection" | sort -n -k 1 | cut -f2 | cut -d' ' -f1 | _forgit_reverse_lines) + [ ${#commits[@]} -eq 0 ] && return 1 + + _forgit_cherry_pick_git_opts=() + _forgit_parse_array _forgit_cherry_pick_git_opts "$FORGIT_CHERRY_PICK_GIT_OPTS" + git cherry-pick "${_forgit_cherry_pick_git_opts[@]}" "${commits[@]}" +} + +_forgit_cherry_pick_from_branch_preview() { + git log --right-only --color=always --cherry-pick --oneline "$1"..."$2" +} + +_forgit_cherry_pick_from_branch() { + _forgit_inside_work_tree || return 1 + local opts branch exitval input_branch args base + + base=$(git branch --show-current) + [[ -z "$base" ]] && echo "Current commit is not on a branch." && return 1 + + args=("$@") + if [[ $# -ne 0 ]]; then + input_branch=${args[0]} + fi + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index --header-lines=1 + --preview=\"$FORGIT cherry_pick_from_branch_preview '$base' {1}\" + $FORGIT_CHERRY_PICK_FROM_BRANCH_FZF_OPTS + " + # loop until either the branch selector is closed or a commit to be cherry + # picked has been selected from within a branch + while true + do + if [[ -z $input_branch ]]; then + branch="$(git branch --color=always --all | + LC_ALL=C sort -k1.1,1.1 -rs | + FZF_DEFAULT_OPTS="$opts" fzf | + awk '{print $1}')" + else + branch=$input_branch + fi + + unset input_branch + [[ -z "$branch" ]] && return 1 + + _forgit_cherry_pick "$branch" + + exitval=$? + [[ $exitval != 130 ]] || [[ $# -ne 0 ]] && return $exitval + done +} + +_forgit_rebase() { + _forgit_inside_work_tree || return 1 + _forgit_contains_non_flags "$@" && { git rebase "$@"; return $?; } + local opts graph target_commit prev_commit + graph=() + [[ $_forgit_log_graph_enable == true ]] && graph=(--graph) + _forgit_rebase_git_opts=() + _forgit_parse_array _forgit_rebase_git_opts "$FORGIT_REBASE_GIT_OPTS" + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index + --bind=\"ctrl-y:execute-silent($FORGIT yank_sha {})\" + --preview=\"$FORGIT file_preview {}\" + $FORGIT_REBASE_FZF_OPTS + " + target_commit=$( + git log "${graph[@]}" --color=always --format="$_forgit_log_format" | + _forgit_emojify | + FZF_DEFAULT_OPTS="$opts" fzf | + _forgit_extract_sha) + if [[ -n "$target_commit" ]]; then + prev_commit=$(_forgit_previous_commit "$target_commit") + git rebase -i "${_forgit_rebase_git_opts[@]}" "$@" "$prev_commit" + fi +} + +_forgit_file_preview() { + local sha + sha=$(echo "$1" | _forgit_extract_sha) + shift + echo "$sha" | xargs -I% git show --color=always % -- "$@" | _forgit_pager show +} + +_forgit_fixup() { + _forgit_inside_work_tree || return 1 + git diff --cached --quiet && echo 'Nothing to fixup: there are no staged changes.' && return 1 + local opts graph quoted_files target_commit prev_commit + graph=() + [[ $_forgit_log_graph_enable == true ]] && graph=(--graph) + _forgit_fixup_git_opts=() + _forgit_parse_array _forgit_fixup_git_opts "$FORGIT_FIXUP_GIT_OPTS" + quoted_files=$(_forgit_quote_files "$@") + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index + --bind=\"ctrl-y:execute-silent($FORGIT yank_sha {})\" + --preview=\"$FORGIT file_preview {} $quoted_files\" + $FORGIT_FIXUP_FZF_OPTS + " + target_commit=$( + git log "${graph[@]}" --color=always --format="$_forgit_log_format" "$@" | + _forgit_emojify | + FZF_DEFAULT_OPTS="$opts" fzf | + _forgit_extract_sha) + if [[ -n "$target_commit" ]] && git commit "${_forgit_fixup_git_opts[@]}" --fixup "$target_commit"; then + prev_commit=$(_forgit_previous_commit "$target_commit") + # rebase will fail if there are unstaged changes so --autostash is needed to temporarily stash them + # GIT_SEQUENCE_EDITOR=: is needed to skip the editor + GIT_SEQUENCE_EDITOR=: git rebase --autostash -i --autosquash "$prev_commit" + fi +} + +_forgit_checkout_file_preview() { + git diff --color=always -- "$1" | _forgit_pager diff +} + +_forgit_git_checkout_file() { + _forgit_checkout_file_git_opts=() + _forgit_parse_array _forgit_checkout_file_git_opts "$FORGIT_CHECKOUT_FILE_GIT_OPTS" + git checkout "${_forgit_checkout_file_git_opts[@]}" "$@" +} + +# git checkout-file selector +_forgit_checkout_file() { + _forgit_inside_work_tree || return 1 + local files opts + [[ $# -ne 0 ]] && { _forgit_git_checkout_file -- "$@"; return $?; } + opts=" + $FORGIT_FZF_DEFAULT_OPTS + -m -0 + --preview=\"$FORGIT checkout_file_preview {}\" + $FORGIT_CHECKOUT_FILE_FZF_OPTS + " + files=() + while IFS='' read -r file; do + files+=("$file") + done < <(_forgit_list_files --modified | + FZF_DEFAULT_OPTS="$opts" fzf) + [[ "${#files[@]}" -gt 0 ]] && _forgit_git_checkout_file "${files[@]}" +} + +_forgit_git_checkout_branch() { + _forgit_checkout_branch_git_opts=() + _forgit_parse_array _forgit_checkout_branch_git_opts "$FORGIT_CHECKOUT_BRANCH_GIT_OPTS" + git checkout "${_forgit_checkout_branch_git_opts[@]}" "$@" +} + +# git checkout-branch selector +_forgit_checkout_branch() { + _forgit_inside_work_tree || return 1 + # if called with arguments, check if branch exists, else create a new one + if [[ $# -ne 0 ]]; then + if [[ "$*" == "-" ]] || git show-branch "$@" &>/dev/null; then + git switch "$@" + else + git switch -c "$@" + fi + checkout_status=$? + git status --short + return $checkout_status + fi + + local opts branch + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index --header-lines=1 + --preview=\"$FORGIT branch_preview {1}\" + $FORGIT_CHECKOUT_BRANCH_FZF_OPTS + " + _forgit_checkout_branch_branch_git_opts=() + _forgit_parse_array _forgit_checkout_branch_branch_git_opts "$FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS" + branch="$(git branch --color=always "${_forgit_checkout_branch_branch_git_opts[@]:---all}" | LC_ALL=C sort -k1.1,1.1 -rs | + FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $1}')" + [[ -z "$branch" ]] && return 1 + + # track the remote branch if possible + if [[ "$branch" == "remotes/"* ]]; then + if git branch | grep -qw "${branch#remotes/*/}"; then + # hack to force creating a new branch which tracks the remote if a local branch already exists + _forgit_git_checkout_branch -b "track/${branch#remotes/*/}" --track "$branch" + elif ! _forgit_git_checkout_branch --track "$branch" 2>/dev/null; then + _forgit_git_checkout_branch "$branch" + fi + else + _forgit_git_checkout_branch "$branch" + fi +} + +_forgit_git_checkout_tag() { + _forgit_checkout_tag_git_opts=() + _forgit_parse_array _forgit_checkout_tag_git_opts "$FORGIT_CHECKOUT_TAG_GIT_OPTS" + git checkout "${_forgit_checkout_tag_git_opts[@]}" "$@" +} + +# git checkout-tag selector +_forgit_checkout_tag() { + _forgit_inside_work_tree || return 1 + local opts + [[ $# -ne 0 ]] && { _forgit_git_checkout_tag "$@"; return $?; } + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index + --preview=\"$FORGIT branch_preview {}\" + $FORGIT_CHECKOUT_TAG_FZF_OPTS + " + tag="$(git tag -l --sort=-v:refname | FZF_DEFAULT_OPTS="$opts" fzf)" + [[ -z "$tag" ]] && return 1 + _forgit_git_checkout_tag "$tag" +} + +_forgit_checkout_commit_preview() { + echo "$1" | _forgit_extract_sha | xargs -I% git show --color=always % | _forgit_pager show +} + +_forgit_git_checkout_commit() { + _forgit_checkout_commit_git_opts=() + _forgit_parse_array _forgit_checkout_commit_git_opts "$FORGIT_CHECKOUT_COMMIT_GIT_OPTS" + git checkout "${_forgit_checkout_commit_git_opts[@]}" "$@" +} + +# git checkout-commit selector +_forgit_checkout_commit() { + _forgit_inside_work_tree || return 1 + local opts graph commit + [[ $# -ne 0 ]] && { _forgit_git_checkout_commit "$@"; return $?; } + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s +m --tiebreak=index + --bind=\"ctrl-y:execute-silent($FORGIT yank_sha {})\" + --preview=\"$FORGIT checkout_commit_preview {}\" + $FORGIT_CHECKOUT_COMMIT_FZF_OPTS + " + graph=() + [[ $_forgit_log_graph_enable == true ]] && graph=(--graph) + commit="$(git log "${graph[@]}" --color=always --format="$_forgit_log_format" | + _forgit_emojify | + FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_sha)" + _forgit_git_checkout_commit "$commit" +} + +_forgit_branch_preview() { + # the trailing '--' ensures that this works for branches that have a name + # that is identical to a file + git log "$1" "${_forgit_log_preview_options[@]}" -- +} + +_forgit_git_branch_delete() { + _forgit_branch_delete_git_opts=() + _forgit_parse_array _forgit_branch_delete_git_opts "$FORGIT_BRANCH_DELETE_GIT_OPTS" + git branch "${_forgit_branch_delete_git_opts[@]}" -D "$@" +} + +_forgit_branch_delete() { + _forgit_inside_work_tree || return 1 + local opts + [[ $# -ne 0 ]] && { _forgit_git_branch_delete "$@"; return $?; } + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + +s --multi --tiebreak=index --header-lines=1 + --preview=\"$FORGIT branch_preview {1}\" + $FORGIT_BRANCH_DELETE_FZF_OPTS + " + + for branch in $(git branch --color=always | + LC_ALL=C sort -k1.1,1.1 -rs | + FZF_DEFAULT_OPTS="$opts" fzf | + awk '{print $1}') + do + _forgit_git_branch_delete "$branch" + done +} + +_forgit_revert_preview() { + echo "$1" | + cut -f2- | + _forgit_extract_sha | + xargs -I% git show --color=always % | + _forgit_pager show +} + +_forgit_git_revert() { + _forgit_revert_commit_git_opts=() + _forgit_parse_array _forgit_revert_commit_git_opts "$FORGIT_REVERT_COMMIT_GIT_OPTS" + git revert "${_forgit_revert_commit_git_opts[@]}" "$@" +} + +# git revert-commit selector +_forgit_revert_commit() { + _forgit_inside_work_tree || return 1 + local opts commits IFS + [[ $# -ne 0 ]] && { _forgit_git_revert "$@"; return $?; } + + opts=" + $FORGIT_FZF_DEFAULT_OPTS + -m +s --tiebreak=index + --ansi --with-nth 2.. + --preview=\"$FORGIT revert_preview {}\" + $FORGIT_REVERT_COMMIT_FZF_OPTS + " + graph=() + [[ $_forgit_log_graph_enable == true ]] && graph=(--graph) + + # in this function, we do something interesting to maintain proper ordering as it's assumed + # you generally want to revert newest->oldest when you multiselect + # The instances of "cut", "nl" and "sort" all serve this purpose + # Please see https://github.com/wfxr/forgit/issues/253 for more details + + commits=() + while IFS='' read -r commit; do + commits+=("$commit") + done < <( + git log "${graph[@]}" --color=always --format="$_forgit_log_format" | + _forgit_emojify | + nl | + FZF_DEFAULT_OPTS="$opts" fzf | + sort -n -k 1 | + cut -f2- | + sed 's/^[^a-f^0-9]*\([a-f0-9]*\).*/\1/') + + [ ${#commits[@]} -eq 0 ] && return 1 + + _forgit_git_revert "${commits[@]}" +} + +_forgit_blame_preview() { + if _forgit_is_file_tracked "$1"; then + _forgit_blame_git_opts=() + _forgit_parse_array _forgit_blame_git_opts "$FORGIT_BLAME_GIT_OPTS" + git blame --date=short "${_forgit_blame_git_opts[@]}" "$@" | _forgit_pager blame + else + echo "File not tracked" + fi +} + +_forgit_git_blame() { + _forgit_blame_git_opts=() + _forgit_parse_array _forgit_blame_git_opts "$FORGIT_BLAME_GIT_OPTS" + git blame "${_forgit_blame_git_opts[@]}" "$@" +} + +# git blame viewer +_forgit_blame() { + _forgit_inside_work_tree || return 1 + local opts flags file + _forgit_contains_non_flags "$@" && { _forgit_git_blame "$@"; return $?; } + flags=() + while IFS='' read -r flag; do + flags+=("$flag") + done < <(git rev-parse --flags "$@") + opts=" + $FORGIT_FZF_DEFAULT_OPTS + --preview=\"$FORGIT blame_preview {} ${flags[*]}\" + $FORGIT_BLAME_FZF_OPTS + " + # flags is not quoted here, which is fine given that they are retrieved + # with git rev-parse and can only contain flags + file=$(FZF_DEFAULT_OPTS="$opts" fzf) + [[ -z "$file" ]] && return 1 + _forgit_git_blame "$file" "${flags[@]}" +} + +# git ignore generator +export FORGIT_GI_REPO_REMOTE=${FORGIT_GI_REPO_REMOTE:-https://github.com/dvcs/gitignore} +export FORGIT_GI_REPO_LOCAL="${FORGIT_GI_REPO_LOCAL:-${XDG_CACHE_HOME:-$HOME/.cache}/forgit/gi/repos/dvcs/gitignore}" +export FORGIT_GI_TEMPLATES=${FORGIT_GI_TEMPLATES:-$FORGIT_GI_REPO_LOCAL/templates} + +_forgit_path_preview() { + local path name ext pager + path=$1 + name=$2 + ext=$3 + pager=$4 + quoted_files=() + while IFS='' read -r file; do + quoted_files+=("'$file'") + done < <(find -L "$path" -type f -name "$name" -o -name "$name$ext") + _forgit_pager "$pager" "${quoted_files[@]}" 2>/dev/null +} + +_forgit_ignore() { + [ -d "$FORGIT_GI_REPO_LOCAL" ] \ + || _forgit_repo_update "$FORGIT_GI_REPO_REMOTE" "$FORGIT_GI_REPO_LOCAL" + local IFS args opts + opts=" + $FORGIT_FZF_DEFAULT_OPTS + -m --preview-window='right:70%' + --preview=\"$FORGIT path_preview $FORGIT_GI_TEMPLATES {2} .gitignore ignore\" + $FORGIT_IGNORE_FZF_OPTS + " + args=("$@") + if [[ $# -eq 0 ]]; then + args=() + while IFS='' read -r arg; do + args+=("$arg") + done < <(_forgit_paths_list "$FORGIT_GI_TEMPLATES" .gitignore | + nl -w4 -s' ' | + FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $2}') + fi + [ ${#args[@]} -eq 0 ] && return 1 + _forgit_path_get "$FORGIT_GI_TEMPLATES" .gitignore "${args[@]}" +} + +# git attributes generator +export FORGIT_ATTR_REPO_REMOTE=${FORGIT_ATTR_REPO_REMOTE:-https://github.com/gitattributes/gitattributes} +export FORGIT_ATTR_REPO_LOCAL=${FORGIT_ATTR_REPO_LOCAL:-${XDG_CACHE_HOME:-$HOME/.cache}/forgit/gat/repos/gitattributes/gitattributes} +export FORGIT_ATTR_TEMPLATES=${FORGIT_ATTR_TEMPLATES:-$FORGIT_ATTR_REPO_LOCAL} + +_forgit_attributes() { + [ -d "$FORGIT_ATTR_REPO_LOCAL" ] \ + || _forgit_repo_update "$FORGIT_ATTR_REPO_REMOTE" "$FORGIT_ATTR_REPO_LOCAL" + local IFS args opts + opts=" + $FORGIT_FZF_DEFAULT_OPTS + -m --preview-window='right:70%' + --preview=\"$FORGIT path_preview $FORGIT_ATTR_TEMPLATES {2} .gitattributes attributes\" + $FORGIT_ATTRIBUTES_FZF_OPTS + " + args=("$@") + if [[ $# -eq 0 ]]; then + args=() + while IFS='' read -r arg; do + args+=("$arg") + done < <(_forgit_paths_list "$FORGIT_ATTR_TEMPLATES" .gitattributes | + nl -w4 -s' ' | + FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $2}') + fi + [ ${#args[@]} -eq 0 ] && return 1 + _forgit_path_get "$FORGIT_ATTR_TEMPLATES" .gitattributes "${args[@]}" +} + +_forgit_repo_update() { + local remote path + remote=$1 + path=$2 + if [[ -d "$path" ]]; then + _forgit_info 'Updating repo...' + (cd "$path" && git pull --no-rebase --ff) || return 1 + else + _forgit_info 'Initializing repo...' + git clone --depth=1 "$remote" "$path" + fi +} + +_forgit_path_get() { + local path ext item filename header + path=$1 + ext=$2 + shift 2 + for item in "$@"; do + if filename=$(find -L "$path" -type f \( -iname "${item}$ext" -o -iname "${item}" \) -print -quit); then + [[ -z "$filename" ]] && _forgit_warn "No template found for '$item'." && continue + header="${filename##*/}" && header="${header%"$ext"}" + echo "### $header" && cat "$filename" && echo + fi + done +} + +_forgit_paths_list() { + local path ext + path=$1 + ext=$2 + find "$path" -name "*$ext" -print |sed -e "s#$ext\$##" -e 's#.*/##' -e '/^$/d' | sort -fu +} + +public_commands=( + "add" + "attributes" + "blame" + "branch_delete" + "checkout_branch" + "checkout_commit" + "checkout_file" + "checkout_tag" + "cherry_pick" + "cherry_pick_from_branch" + "clean" + "diff" + "fixup" + "ignore" + "log" + "reflog" + "rebase" + "reset_head" + "revert_commit" + "show" + "stash_show" + "stash_push" +) + +private_commands=( + "add_preview" + "blame_preview" + "branch_preview" + "checkout_commit_preview" + "checkout_file_preview" + "cherry_pick_from_branch_preview" + "cherry_pick_preview" + "clean_preview" + "diff_enter" + "exec_show" + "file_preview" + "path_preview" + "revert_preview" + "reset_head_preview" + "show_enter" + "show_preview" + "stash_push_preview" + "stash_show_preview" + "yank_sha" + "yank_stash_name" + "log_preview" + "log_enter" + "exec_diff" + "diff_view" + "edit_diffed_file" + "edit_add_file" + "pager" +) + +cmd="$1" +shift + +# shellcheck disable=SC2076 +if [[ ! " ${public_commands[*]} " =~ " ${cmd} " ]] && [[ ! " ${private_commands[*]} " =~ " ${cmd} " ]]; then + if [[ -z "$cmd" ]]; then + printf "forgit: missing command\n\n" + else + printf "forgit: '%s' is not a valid forgit command.\n\n" "$cmd" + fi + printf "The following commands are supported:\n" + printf "\t%s\n" "${public_commands[@]}" + exit 1 +fi + +_forgit_"${cmd}" "$@" diff --git a/.config/fish/fish_plugins b/.config/fish/fish_plugins index f195f4a7..19bf9a86 100644 --- a/.config/fish/fish_plugins +++ b/.config/fish/fish_plugins @@ -10,5 +10,6 @@ paldepind/projectdo wfxr/forgit jorgebucaran/nvm.fish danhper/fish-ssh-agent +~ficd/update.fish ~ficd/fisher ~ficd/jrnl.fish diff --git a/.config/fish/fish_variables b/.config/fish/fish_variables index 82093570..09e8b422 100644 --- a/.config/fish/fish_variables +++ b/.config/fish/fish_variables @@ -6,20 +6,21 @@ SETUVAR Z_DATA_DIR:/home/fic/\x2elocal/share/z SETUVAR __fish_initialized:3800 SETUVAR _fisher__7E_ficd_2F_fisher_files:/home/fic/\x2econfig/fish/functions/fisher\x2efish\x1e/home/fic/\x2econfig/fish/completions/fisher\x2efish SETUVAR _fisher__7E_ficd_2F_jrnl_2E_fish_files:/home/fic/\x2econfig/fish/functions/jrnl\x2efish\x1e/home/fic/\x2econfig/fish/completions/jrnl\x2efish -SETUVAR _fisher_danhper_2F_fish_2D_ssh_2D_agent_files:\x7e/\x2econfig/fish/functions/__ssh_agent_is_started\x2efish\x1e\x7e/\x2econfig/fish/functions/__ssh_agent_start\x2efish\x1e\x7e/\x2econfig/fish/conf\x2ed/fish\x2dssh\x2dagent\x2efish -SETUVAR _fisher_franciscolourenco_2F_done_files:\x7e/\x2econfig/fish/conf\x2ed/done\x2efish -SETUVAR _fisher_jorgebucaran_2F_autopair_2E_fish_files:\x7e/\x2econfig/fish/functions/_autopair_backspace\x2efish\x1e\x7e/\x2econfig/fish/functions/_autopair_insert_left\x2efish\x1e\x7e/\x2econfig/fish/functions/_autopair_insert_right\x2efish\x1e\x7e/\x2econfig/fish/functions/_autopair_insert_same\x2efish\x1e\x7e/\x2econfig/fish/functions/_autopair_tab\x2efish\x1e\x7e/\x2econfig/fish/conf\x2ed/autopair\x2efish -SETUVAR _fisher_jorgebucaran_2F_nvm_2E_fish_files:\x7e/\x2econfig/fish/functions/_nvm_index_update\x2efish\x1e\x7e/\x2econfig/fish/functions/_nvm_list\x2efish\x1e\x7e/\x2econfig/fish/functions/_nvm_version_activate\x2efish\x1e\x7e/\x2econfig/fish/functions/_nvm_version_deactivate\x2efish\x1e\x7e/\x2econfig/fish/functions/nvm\x2efish\x1e\x7e/\x2econfig/fish/conf\x2ed/nvm\x2efish\x1e\x7e/\x2econfig/fish/completions/nvm\x2efish -SETUVAR _fisher_jorgebucaran_2F_replay_2E_fish_files:\x7e/\x2econfig/fish/functions/replay\x2efish\x1e\x7e/\x2econfig/fish/completions/replay\x2efish -SETUVAR _fisher_jorgebucaran_2F_spark_2E_fish_files:\x7e/\x2econfig/fish/functions/spark\x2efish\x1e\x7e/\x2econfig/fish/completions/spark\x2efish -SETUVAR _fisher_joseluisq_2F_gitnow_40_32_2E_31_32_2E_30__files:\x7e/\x2econfig/fish/functions/__gitnow_check_if_branch_exist\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_clone_msg\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_clone_params\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_clone_repo\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_current_branch_list\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_current_branch_name\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_current_remote\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_get_clip_program\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_get_latest_semver_release_tag\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_get_latest_tag\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_get_tags_ordered\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_get_valid_semver_prerelease_value\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_get_valid_semver_release_value\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_gitflow_branch\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_has_uncommited_changes\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_increment_number\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_is_git_repository\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_is_number\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_load_config\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_load_git_functions\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_manual\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_msg_not_valid_repository\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_new_branch_switch\x2efish\x1e\x7e/\x2econfig/fish/functions/__gitnow_slugify\x2efish\x1e\x7e/\x2econfig/fish/conf\x2ed/gitnow\x2efish\x1e\x7e/\x2econfig/fish/completions/merge\x2efish\x1e\x7e/\x2econfig/fish/completions/move\x2efish\x1e\x7e/\x2econfig/fish/completions/tag\x2efish -SETUVAR _fisher_meaningful_2D_ooo_2F_sponge_files:\x7e/\x2econfig/fish/functions/_sponge_clear_state\x2efish\x1e\x7e/\x2econfig/fish/functions/_sponge_on_exit\x2efish\x1e\x7e/\x2econfig/fish/functions/_sponge_on_postexec\x2efish\x1e\x7e/\x2econfig/fish/functions/_sponge_on_preexec\x2efish\x1e\x7e/\x2econfig/fish/functions/_sponge_on_prompt\x2efish\x1e\x7e/\x2econfig/fish/functions/_sponge_remove_from_history\x2efish\x1e\x7e/\x2econfig/fish/functions/sponge_filter_failed\x2efish\x1e\x7e/\x2econfig/fish/functions/sponge_filter_matched\x2efish\x1e\x7e/\x2econfig/fish/conf\x2ed/sponge\x2efish -SETUVAR _fisher_nickeb96_2F_puffer_2D_fish_files:\x7e/\x2econfig/fish/functions/_puffer_fish_expand_bang\x2efish\x1e\x7e/\x2econfig/fish/functions/_puffer_fish_expand_dots\x2efish\x1e\x7e/\x2econfig/fish/functions/_puffer_fish_expand_lastarg\x2efish\x1e\x7e/\x2econfig/fish/conf\x2ed/puffer_fish_key_bindings\x2efish -SETUVAR _fisher_paldepind_2F_projectdo_files:\x7e/\x2econfig/fish/functions/projectdo_build\x2efish\x1e\x7e/\x2econfig/fish/functions/projectdo_run\x2efish\x1e\x7e/\x2econfig/fish/functions/projectdo_test\x2efish\x1e\x7e/\x2econfig/fish/functions/projectdo_tool\x2efish\x1e\x7e/\x2econfig/fish/completions/projectdo\x2efish -SETUVAR _fisher_patrickf1_2F_fzf_2E_fish_files:\x7e/\x2econfig/fish/functions/_fzf_configure_bindings_help\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_extract_var_info\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_preview_changed_file\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_preview_file\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_report_diff_type\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_report_file_type\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_search_directory\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_search_git_log\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_search_git_status\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_search_history\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_search_processes\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_search_variables\x2efish\x1e\x7e/\x2econfig/fish/functions/_fzf_wrapper\x2efish\x1e\x7e/\x2econfig/fish/functions/fzf_configure_bindings\x2efish\x1e\x7e/\x2econfig/fish/conf\x2ed/fzf\x2efish\x1e\x7e/\x2econfig/fish/completions/fzf_configure_bindings\x2efish -SETUVAR _fisher_plugins:patrickf1/fzf\x2efish\x1efranciscolourenco/done\x1ejorgebucaran/replay\x2efish\x1ejorgebucaran/spark\x2efish\x1ejoseluisq/gitnow\x402\x2e12\x2e0\x1emeaningful\x2dooo/sponge\x1ejorgebucaran/autopair\x2efish\x1enickeb96/puffer\x2dfish\x1epaldepind/projectdo\x1ewfxr/forgit\x1ejorgebucaran/nvm\x2efish\x1edanhper/fish\x2dssh\x2dagent\x1e\x7eficd/fisher\x1e\x7eficd/jrnl\x2efish +SETUVAR _fisher__7E_ficd_2F_update_2E_fish_files:/home/fic/\x2econfig/fish/functions/update\x2efish\x1e/home/fic/\x2econfig/fish/completions/update\x2efish +SETUVAR _fisher_danhper_2F_fish_2D_ssh_2D_agent_files:/home/fic/\x2econfig/fish/functions/__ssh_agent_is_started\x2efish\x1e/home/fic/\x2econfig/fish/functions/__ssh_agent_start\x2efish\x1e/home/fic/\x2econfig/fish/conf\x2ed/fish\x2dssh\x2dagent\x2efish +SETUVAR _fisher_franciscolourenco_2F_done_files:/home/fic/\x2econfig/fish/conf\x2ed/done\x2efish +SETUVAR _fisher_jorgebucaran_2F_autopair_2E_fish_files:/home/fic/\x2econfig/fish/functions/_autopair_backspace\x2efish\x1e/home/fic/\x2econfig/fish/functions/_autopair_insert_left\x2efish\x1e/home/fic/\x2econfig/fish/functions/_autopair_insert_right\x2efish\x1e/home/fic/\x2econfig/fish/functions/_autopair_insert_same\x2efish\x1e/home/fic/\x2econfig/fish/functions/_autopair_tab\x2efish\x1e/home/fic/\x2econfig/fish/conf\x2ed/autopair\x2efish +SETUVAR _fisher_jorgebucaran_2F_nvm_2E_fish_files:/home/fic/\x2econfig/fish/functions/_nvm_index_update\x2efish\x1e/home/fic/\x2econfig/fish/functions/_nvm_list\x2efish\x1e/home/fic/\x2econfig/fish/functions/_nvm_version_activate\x2efish\x1e/home/fic/\x2econfig/fish/functions/_nvm_version_deactivate\x2efish\x1e/home/fic/\x2econfig/fish/functions/nvm\x2efish\x1e/home/fic/\x2econfig/fish/conf\x2ed/nvm\x2efish\x1e/home/fic/\x2econfig/fish/completions/nvm\x2efish +SETUVAR _fisher_jorgebucaran_2F_replay_2E_fish_files:/home/fic/\x2econfig/fish/functions/replay\x2efish\x1e/home/fic/\x2econfig/fish/completions/replay\x2efish +SETUVAR _fisher_jorgebucaran_2F_spark_2E_fish_files:/home/fic/\x2econfig/fish/functions/spark\x2efish\x1e/home/fic/\x2econfig/fish/completions/spark\x2efish +SETUVAR _fisher_joseluisq_2F_gitnow_40_32_2E_31_32_2E_30__files:/home/fic/\x2econfig/fish/functions/__gitnow_check_if_branch_exist\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_clone_msg\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_clone_params\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_clone_repo\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_current_branch_list\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_current_branch_name\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_current_remote\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_get_clip_program\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_get_latest_semver_release_tag\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_get_latest_tag\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_get_tags_ordered\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_get_valid_semver_prerelease_value\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_get_valid_semver_release_value\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_gitflow_branch\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_has_uncommited_changes\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_increment_number\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_is_git_repository\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_is_number\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_load_config\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_load_git_functions\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_manual\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_msg_not_valid_repository\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_new_branch_switch\x2efish\x1e/home/fic/\x2econfig/fish/functions/__gitnow_slugify\x2efish\x1e/home/fic/\x2econfig/fish/conf\x2ed/gitnow\x2efish\x1e/home/fic/\x2econfig/fish/completions/merge\x2efish\x1e/home/fic/\x2econfig/fish/completions/move\x2efish\x1e/home/fic/\x2econfig/fish/completions/tag\x2efish +SETUVAR _fisher_meaningful_2D_ooo_2F_sponge_files:/home/fic/\x2econfig/fish/functions/_sponge_clear_state\x2efish\x1e/home/fic/\x2econfig/fish/functions/_sponge_on_exit\x2efish\x1e/home/fic/\x2econfig/fish/functions/_sponge_on_postexec\x2efish\x1e/home/fic/\x2econfig/fish/functions/_sponge_on_preexec\x2efish\x1e/home/fic/\x2econfig/fish/functions/_sponge_on_prompt\x2efish\x1e/home/fic/\x2econfig/fish/functions/_sponge_remove_from_history\x2efish\x1e/home/fic/\x2econfig/fish/functions/sponge_filter_failed\x2efish\x1e/home/fic/\x2econfig/fish/functions/sponge_filter_matched\x2efish\x1e/home/fic/\x2econfig/fish/conf\x2ed/sponge\x2efish +SETUVAR _fisher_nickeb96_2F_puffer_2D_fish_files:/home/fic/\x2econfig/fish/functions/_puffer_fish_expand_bang\x2efish\x1e/home/fic/\x2econfig/fish/functions/_puffer_fish_expand_dots\x2efish\x1e/home/fic/\x2econfig/fish/functions/_puffer_fish_expand_lastarg\x2efish\x1e/home/fic/\x2econfig/fish/conf\x2ed/puffer_fish_key_bindings\x2efish +SETUVAR _fisher_paldepind_2F_projectdo_files:/home/fic/\x2econfig/fish/functions/projectdo_build\x2efish\x1e/home/fic/\x2econfig/fish/functions/projectdo_run\x2efish\x1e/home/fic/\x2econfig/fish/functions/projectdo_test\x2efish\x1e/home/fic/\x2econfig/fish/functions/projectdo_tool\x2efish\x1e/home/fic/\x2econfig/fish/completions/projectdo\x2efish +SETUVAR _fisher_patrickf1_2F_fzf_2E_fish_files:/home/fic/\x2econfig/fish/functions/_fzf_configure_bindings_help\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_extract_var_info\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_preview_changed_file\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_preview_file\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_report_diff_type\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_report_file_type\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_search_directory\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_search_git_log\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_search_git_status\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_search_history\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_search_processes\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_search_variables\x2efish\x1e/home/fic/\x2econfig/fish/functions/_fzf_wrapper\x2efish\x1e/home/fic/\x2econfig/fish/functions/fzf_configure_bindings\x2efish\x1e/home/fic/\x2econfig/fish/conf\x2ed/fzf\x2efish\x1e/home/fic/\x2econfig/fish/completions/fzf_configure_bindings\x2efish +SETUVAR _fisher_plugins:patrickf1/fzf\x2efish\x1efranciscolourenco/done\x1ejorgebucaran/replay\x2efish\x1ejorgebucaran/spark\x2efish\x1ejoseluisq/gitnow\x402\x2e12\x2e0\x1emeaningful\x2dooo/sponge\x1ejorgebucaran/autopair\x2efish\x1enickeb96/puffer\x2dfish\x1epaldepind/projectdo\x1ewfxr/forgit\x1ejorgebucaran/nvm\x2efish\x1edanhper/fish\x2dssh\x2dagent\x1e\x7eficd/update\x2efish\x1e\x7eficd/fisher\x1e\x7eficd/jrnl\x2efish SETUVAR _fisher_upgraded_to_4_4:\x1d -SETUVAR _fisher_wfxr_2F_forgit_files:\x7e/\x2econfig/fish/conf\x2ed/bin\x1e\x7e/\x2econfig/fish/conf\x2ed/forgit\x2eplugin\x2efish\x1e\x7e/\x2econfig/fish/completions/_git\x2dforgit\x1e\x7e/\x2econfig/fish/completions/git\x2dforgit\x2ebash\x1e\x7e/\x2econfig/fish/completions/git\x2dforgit\x2efish +SETUVAR _fisher_wfxr_2F_forgit_files:/home/fic/\x2econfig/fish/conf\x2ed/bin\x1e/home/fic/\x2econfig/fish/conf\x2ed/forgit\x2eplugin\x2efish\x1e/home/fic/\x2econfig/fish/completions/_git\x2dforgit\x1e/home/fic/\x2econfig/fish/completions/git\x2dforgit\x2ebash\x1e/home/fic/\x2econfig/fish/completions/git\x2dforgit\x2efish SETUVAR abbr_path:/home/fic/\x2econfig/fish/conf\x2ed/abbr\x2efish SETUVAR cac_ip:172\x2e30\x2e90\x2e10 SETUVAR fish_color_autosuggestion:89492A diff --git a/.config/fish/functions/update.fish b/.config/fish/functions/update.fish new file mode 100644 index 00000000..c8eabd54 --- /dev/null +++ b/.config/fish/functions/update.fish @@ -0,0 +1,75 @@ +function update --description "Update everything" + # autoconfirm + # or force no multiterm + argparse h/help y/no-confirm o/one-term -- $argv + if set -ql _flag_h + printf '%s\n' \ + 'update -- update everything' \ + 'Usage: run `update` to begin' \ + 'Options:' \ + '-y/--no-confirm: Skip update confirmations' \ + '-o/--one-term: Force single terminal mode' \ + '-h/--help: Print this screen' + return 0 + end + # TODO add way for user to configure the update list? + set -g termcmd foot fish -c + set -l std 'cargo install-update -a +gup update +uv tool upgrade --all' + set -l noconfirm 'flatpak update -y +yes | paru -Syu' + set -l confirm 'flatpak update +paru -Syu' + set -g cmds (string split \n -- $std | string trim) + if set -q _flag_y + set --append cmds (string split \n -- $noconfirm | string trim) + sudo -v + else + set --append cmds (string split \n -- $confirm | string trim) + end + echo Commands to be run: + for i in $cmds + echo $i + end + + function execute + argparse i/interactive -- $argv + if set -ql _flag_i + for cmd in $cmds + # max 2 jobs at a time + while test (count (jobs -p)) -ge 2 + sleep 0.5 + end + if string match -q "*paru *" -- "$cmd" + eval $cmd + else + $termcmd $cmd &>/dev/null & + end + end + else + for cmd in $cmds + eval $cmd + end + end + end + + if not set -ql _flag_o && isatty stdin && isatty stdout + while read --nchars 1 -l response --prompt-str="Run on multiple terminals? (y/n)" or return 1 + switch $response + case y Y + execute --interactive + wait + break + case n N + execute + break + case '*' + echo Invalid input! + continue + end + end + else + execute + end +end