From f927c223f3bf3f15751a6354fee34b294732d6c2 Mon Sep 17 00:00:00 2001 From: sandroid Date: Sat, 3 Feb 2024 00:16:45 +0100 Subject: [PATCH] Refactor: Move git commands from deferred code into functions We often used deferred code to encapsulate git commands and make them reusable. This change removes deferred code for git commands and replaces it with functions instead. Some of the deferred code was used with xargs, which executes it on a subshell. To avoid having to expose the new git functions the same way we do with the preview functions, the usage of xargs in these cases is replaced with either a loop or a single command when possible. --- bin/git-forgit | 183 +++++++++++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 73 deletions(-) diff --git a/bin/git-forgit b/bin/git-forgit index be5341e..31482ee 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -236,6 +236,12 @@ _forgit_add_preview() { 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 @@ -247,12 +253,9 @@ _forgit_get_single_file_from_add_line() { # git add selector _forgit_add() { _forgit_inside_work_tree || return 1 - local git_add changed unmerged untracked files opts extract - _forgit_add_git_opts=() - _forgit_parse_array _forgit_add_git_opts "$FORGIT_ADD_GIT_OPTS" - git_add="git add ${_forgit_add_git_opts[*]}" + local changed unmerged untracked files opts extract # Add files if passed as arguments - [[ $# -ne 0 ]] && { $git_add "$@" && git status -su; return $?; } + [[ $# -ne 0 ]] && { _forgit_git_add "$@" && git status -su; return $?; } changed=$(git config --get-color color.status.changed red) unmerged=$(git config --get-color color.status.unmerged red) @@ -275,7 +278,7 @@ _forgit_add() { sed -E 's/^(..[^[:space:]]*)[[:space:]]+(.*)$/[\1] \2/' | FZF_DEFAULT_OPTS="$opts" fzf | sh -c "$extract") - [[ -n "$files" ]] && echo "$files"| tr '\n' '\0' | $git_add --pathspec-file-nul --pathspec-from-file - && git status -su && return + [[ -n "$files" ]] && echo "$files"| tr '\n' '\0' | _forgit_git_add --pathspec-file-nul --pathspec-from-file - && git status -su && return echo 'Nothing to add.' } @@ -284,14 +287,17 @@ _forgit_reset_head_preview() { git diff --staged --color=always -- "$file" | $_forgit_diff_pager } +_forgit_git_reset_head() { + _forgit_reset_head_git_opts=() + _forgit_parse_array _forgit_reset_head_git_opts "$FORGIT_RESET_HEAD_GIT_OPTS" + git reset -q "${_forgit_reset_head_git_opts[@]}" HEAD "$@" +} + # git reset HEAD (unstage) selector _forgit_reset_head() { _forgit_inside_work_tree || return 1 - local git_reset_head files opts rootdir - _forgit_reset_head_git_opts=() - _forgit_parse_array _forgit_reset_head_git_opts "$FORGIT_RESET_HEAD_GIT_OPTS" - git_reset_head="git reset -q ${_forgit_reset_head_git_opts[*]} HEAD" - [[ $# -ne 0 ]] && { $git_reset_head "$@" && git status --short; return $?; } + local files opts rootdir + [[ $# -ne 0 ]] && { _forgit_git_reset_head "$@" && git status --short; return $?; } rootdir=$(git rev-parse --show-toplevel) opts=" $FORGIT_FZF_DEFAULT_OPTS @@ -299,10 +305,18 @@ _forgit_reset_head() { --preview=\"$FORGIT reset_head_preview \"$rootdir\"/{}\" $FORGIT_RESET_HEAD_FZF_OPTS " - files="$(git diff --staged --name-only | FZF_DEFAULT_OPTS="$opts" fzf)" - # shellcheck disable=2086 - [[ -n "$files" ]] && echo "$files" | tr '\n' '\0' | xargs -0 -I% $git_reset_head "$rootdir"/% && git status --short && return - echo 'Nothing to unstage.' + files=() + while IFS='' read -r file; do + files+=("$file") + done < <(git diff --staged --name-only | FZF_DEFAULT_OPTS="$opts" fzf) + if [[ ${#files} -eq 0 ]]; then + echo 'Nothing to unstage.' + return 1 + fi + for file in "${files[@]}"; do + _forgit_git_reset_head "$rootdir/$file" + done + git status --short } _forgit_stash_show_preview() { @@ -318,9 +332,8 @@ _forgit_git_stash_show() { # git stash viewer _forgit_stash_show() { _forgit_inside_work_tree || return 1 - local git_stash_show git_stash_list opts - git_stash_show="git stash show --color=always --ext-diff" - [[ $# -ne 0 ]] && { $git_stash_show "$@"; return $?; } + local git_stash_list opts + [[ $# -ne 0 ]] && { _forgit_git_stash_show "$@"; return $?; } _forgit_stash_show_git_opts=() _forgit_parse_array _forgit_stash_show_git_opts "$FORGIT_STASH_SHOW_GIT_OPTS" git_stash_list="git stash list ${_forgit_stash_show_git_opts[*]}" @@ -346,13 +359,16 @@ _forgit_stash_push_preview() { fi } +_forgit_git_stash_push() { + _forgit_stash_push_git_opts=() + _forgit_parse_array _forgit_stash_push_git_opts "$FORGIT_STASH_PUSH_GIT_OPTS" + git stash push "${_forgit_stash_push_git_opts[@]}" "$@" +} + # git stash push selector _forgit_stash_push() { _forgit_inside_work_tree || return 1 - local git_stash_push msg args - _forgit_stash_push_git_opts=() - _forgit_parse_array _forgit_stash_push_git_opts "$FORGIT_STASH_PUSH_GIT_OPTS" - git_stash_push="git stash push ${_forgit_stash_push_git_opts[*]}" + local msg args args=( "$@" ) while (( "$#" )); do case "$1" in @@ -364,7 +380,7 @@ _forgit_stash_push() { # ignore -u as it's used implicitly -u|--include-untracked) shift ;; # pass to git directly when encountering anything else - *) $git_stash_push "${args[@]}"; return $? + *) _forgit_git_stash_push "${args[@]}"; return $? esac done local opts files @@ -376,7 +392,7 @@ _forgit_stash_push() { # Show both modified and untracked files files=$(git ls-files --exclude-standard --modified --others | FZF_DEFAULT_OPTS="$opts" fzf --preview="$FORGIT stash_push_preview {}") [[ -z "$files" ]] && return 1 - echo "${files[@]}" | tr '\n' '\0' | $git_stash_push ${msg:+-m "$msg"} -u --pathspec-file-nul --pathspec-from-file - + echo "${files[@]}" | tr '\n' '\0' | _forgit_git_stash_push ${msg:+-m "$msg"} -u --pathspec-file-nul --pathspec-from-file - } # git clean selector @@ -405,6 +421,7 @@ _forgit_cherry_pick_preview() { _forgit_cherry_pick() { local git_cherry_pick base target opts fzf_selection fzf_exitval + _forgit_cherry_pick_git_opts=() _forgit_parse_array _forgit_cherry_pick_git_opts "$FORGIT_CHERRY_PICK_GIT_OPTS" git_cherry_pick="git cherry-pick ${_forgit_cherry_pick_git_opts[*]}" @@ -549,14 +566,17 @@ _forgit_checkout_file_preview() { git diff --color=always -- "$1" | $_forgit_diff_pager } +_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 git_checkout files opts - _forgit_checkout_file_git_opts=() - _forgit_parse_array _forgit_checkout_file_git_opts "$FORGIT_CHECKOUT_FILE_GIT_OPTS" - git_checkout="git checkout ${_forgit_checkout_file_git_opts[*]}" - [[ $# -ne 0 ]] && { $git_checkout -- "$@"; return $?; } + local files opts + [[ $# -ne 0 ]] && { _forgit_git_checkout_file -- "$@"; return $?; } opts=" $FORGIT_FZF_DEFAULT_OPTS -m -0 @@ -564,7 +584,13 @@ _forgit_checkout_file() { $FORGIT_CHECKOUT_FILE_FZF_OPTS " files="$(git ls-files --modified "$(git rev-parse --show-toplevel)"| FZF_DEFAULT_OPTS="$opts" fzf)" - [[ -n "$files" ]] && echo "$files" | tr '\n' '\0' | $git_checkout --pathspec-file-nul --pathspec-from-file - + [[ -n "$files" ]] && echo "$files" | tr '\n' '\0' | _forgit_git_checkout_file --pathspec-file-nul --pathspec-from-file - +} + +_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 @@ -582,7 +608,7 @@ _forgit_checkout_branch() { return $checkout_status fi - local git_checkout cmd opts branch + local cmd opts branch _forgit_checkout_branch_branch_git_opts=() _forgit_parse_array _forgit_checkout_branch_branch_git_opts "$FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS" cmd="git branch --color=always ${_forgit_checkout_branch_branch_git_opts[*]:---all} | LC_ALL=C sort -k1.1,1.1 -rs" @@ -595,30 +621,30 @@ _forgit_checkout_branch() { branch="$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $1}')" [[ -z "$branch" ]] && return 1 - _forgit_checkout_branch_git_opts=() - _forgit_parse_array _forgit_checkout_branch_git_opts "$FORGIT_CHECKOUT_BRANCH_GIT_OPTS" - git_checkout="git checkout ${_forgit_checkout_branch_git_opts[*]}" # track the remote branch if possible if [[ "$branch" == "remotes/origin/"* ]]; then if git branch | grep -qw "${branch#remotes/origin/}"; then # hack to force creating a new branch which tracks the remote if a local branch already exists - $git_checkout -b "track/${branch#remotes/origin/}" --track "$branch" - elif ! $git_checkout --track "$branch" 2>/dev/null; then - $git_checkout "$branch" + _forgit_git_checkout_branch -b "track/${branch#remotes/origin/}" --track "$branch" + elif ! _forgit_git_checkout_branch --track "$branch" 2>/dev/null; then + _forgit_git_checkout_branch "$branch" fi else - $git_checkout "$branch" + _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 git_checkout cmd opts - _forgit_checkout_tag_git_opts=() - _forgit_parse_array _forgit_checkout_tag_git_opts "$FORGIT_CHECKOUT_TAG_GIT_OPTS" - git_checkout="git checkout ${_forgit_checkout_tag_git_opts[*]}" - [[ $# -ne 0 ]] && { $git_checkout "$@"; return $?; } + local cmd opts + [[ $# -ne 0 ]] && { _forgit_git_checkout_tag "$@"; return $?; } cmd="git tag -l --sort=-v:refname" opts=" $FORGIT_FZF_DEFAULT_OPTS @@ -628,21 +654,24 @@ _forgit_checkout_tag() { " tag="$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" fzf)" [[ -z "$tag" ]] && return 1 - $git_checkout "$tag" + _forgit_git_checkout_tag "$tag" } _forgit_checkout_commit_preview() { echo "$1" | _forgit_extract_sha | xargs -I% git show --color=always % | $_forgit_show_pager } +_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 git_checkout opts graph - _forgit_checkout_commit_git_opts=() - _forgit_parse_array _forgit_checkout_commit_git_opts "$FORGIT_CHECKOUT_COMMIT_GIT_OPTS" - git_checkout="git checkout ${_forgit_checkout_commit_git_opts[*]}" - [[ $# -ne 0 ]] && { $git_checkout "$@"; return $?; } + local opts graph commit + [[ $# -ne 0 ]] && { _forgit_git_checkout_commit "$@"; return $?; } opts=" $FORGIT_FZF_DEFAULT_OPTS +s +m --tiebreak=index @@ -652,22 +681,25 @@ _forgit_checkout_commit() { " graph=--graph [[ $FORGIT_LOG_GRAPH_ENABLE == false ]] && graph= - # shellcheck disable=2086 - eval "git log $graph --color=always --format='$_forgit_log_format' $_forgit_emojify" | - FZF_DEFAULT_OPTS="$opts" fzf | eval "$_forgit_extract_sha" | xargs -I% $git_checkout % -- + commit="$(eval "git log $graph --color=always --format='$_forgit_log_format' $_forgit_emojify" | + FZF_DEFAULT_OPTS="$opts" fzf | eval "$_forgit_extract_sha")" + _forgit_git_checkout_commit "$commit" } _forgit_branch_preview() { git log "$1" "${_forgit_log_preview_options[@]}" } -_forgit_branch_delete() { - _forgit_inside_work_tree || return 1 - local git_branch opts cmd branches +_forgit_git_branch_delete() { _forgit_branch_delete_git_opts=() _forgit_parse_array _forgit_branch_delete_git_opts "$FORGIT_BRANCH_DELETE_GIT_OPTS" - git_branch="git branch ${_forgit_branch_delete_git_opts[*]}" - [[ $# -ne 0 ]] && { $git_branch -D "$@"; return $?; } + git branch "${_forgit_branch_delete_git_opts[@]}" -D "$@" +} + +_forgit_branch_delete() { + _forgit_inside_work_tree || return 1 + local opts cmd + [[ $# -ne 0 ]] && { _forgit_git_branch_delete "$@"; return $?; } opts=" $FORGIT_FZF_DEFAULT_OPTS @@ -677,9 +709,9 @@ _forgit_branch_delete() { " cmd="git branch --color=always | LC_ALL=C sort -k1.1,1.1 -rs" - branches=$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $1}') - # shellcheck disable=2086 - echo -n "$branches" | tr '\n' '\0' | xargs -I{} -0 $git_branch -D {} + for branch in $(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $1}'); do + _forgit_git_branch_delete "$branch" + done } _forgit_revert_preview() { @@ -690,14 +722,17 @@ _forgit_revert_preview() { $_forgit_show_pager } +_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 git_revert cmd opts files commits IFS - _forgit_revert_commit_git_opts=() - _forgit_parse_array _forgit_revert_commit_git_opts "$FORGIT_REVERT_COMMIT_GIT_OPTS" - git_revert="git revert ${_forgit_revert_commit_git_opts[*]}" - [[ $# -ne 0 ]] && { $git_revert "$@"; return $?; } + local cmd opts files commits IFS + [[ $# -ne 0 ]] && { _forgit_git_revert "$@"; return $?; } cmd="git log --graph --color=always --format='$_forgit_log_format' $* $_forgit_emojify" opts=" @@ -727,7 +762,7 @@ _forgit_revert_commit() { [ ${#commits[@]} -eq 0 ] && return 1 - $git_revert "${commits[@]}" + _forgit_git_revert "${commits[@]}" } _forgit_blame_preview() { @@ -740,14 +775,17 @@ _forgit_blame_preview() { 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 git_blame opts flags file - _forgit_blame_git_opts=() - _forgit_parse_array _forgit_blame_git_opts "$FORGIT_BLAME_GIT_OPTS" - git_blame="git blame ${_forgit_blame_git_opts[*]}" - _forgit_contains_non_flags "$@" && { $git_blame "$@"; return $?; } + local opts flags file + _forgit_contains_non_flags "$@" && { _forgit_git_blame "$@"; return $?; } opts=" $FORGIT_FZF_DEFAULT_OPTS $FORGIT_BLAME_FZF_OPTS @@ -761,8 +799,7 @@ _forgit_blame() { # with git rev-parse and can only contain flags file=$(FZF_DEFAULT_OPTS="$opts" fzf --preview="$FORGIT blame_preview {} ${flags[*]}") [[ -z "$file" ]] && return 1 - # shellcheck disable=2086 - eval $git_blame "$file" "${flags[*]}" + _forgit_git_blame "$file" "${flags[@]}" } # git ignore generator