ci: enforce consistent shell formatting with shfmt (#514)

This commit is contained in:
sandr01d 2026-04-11 17:51:34 +02:00 committed by GitHub
parent 7663f3a809
commit dfe682e113
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 322 additions and 261 deletions

View file

@ -6,4 +6,7 @@ end_of_line = lf
indent_style = space indent_style = space
insert_final_newline = true insert_final_newline = true
tab_width = 4 tab_width = 4
indent_size = 4
simplify = true
switch_case_indent = true
trim_trailing_whitespace = true trim_trailing_whitespace = true

View file

@ -78,3 +78,13 @@ jobs:
run: npm install -g @commitlint/cli @commitlint/config-conventional run: npm install -g @commitlint/cli @commitlint/config-conventional
- name: Validate PR commits - name: Validate PR commits
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check format
uses: mfinelli/setup-shfmt@v4
with:
shfmt-version: 3.13.1
- run: shfmt --diff .

View file

@ -62,6 +62,7 @@ lib/bashunit .
bash forgit.plugin.sh bash forgit.plugin.sh
zsh forgit.plugin.zsh zsh forgit.plugin.zsh
fish conf.d/forgit.plugin.fish fish conf.d/forgit.plugin.fish
shfmt --write .
``` ```
In your pull request, report which shells and operating systems you tested. In your pull request, report which shells and operating systems you tested.

View file

@ -39,8 +39,11 @@ _forgit_inside_work_tree() { git rev-parse --is-inside-work-tree >/dev/null 2>&1
_forgit_inside_git_dir() { git rev-parse --is-inside-git-dir >/dev/null 2>&1; } _forgit_inside_git_dir() { git rev-parse --is-inside-git-dir >/dev/null 2>&1; }
_forgit_inside_git_repo() { _forgit_inside_work_tree || _forgit_inside_git_dir; } _forgit_inside_git_repo() { _forgit_inside_work_tree || _forgit_inside_git_dir; }
# tac is not available on OSX, tail -r is not available on Linux, so we use either of them # 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_reverse_lines() { tac 2>/dev/null || tail -r; }
_forgit_strip_ansi() { local ESC=$'\033'; sed "s/${ESC}\[[0-9;]*m//g"; } _forgit_strip_ansi() {
local ESC=$'\033'
sed "s/${ESC}\[[0-9;]*m//g"
}
_forgit_previous_commit() { _forgit_previous_commit() {
# "SHA~" is invalid when the commit is the first commit, but we can use "--root" instead # "SHA~" is invalid when the commit is the first commit, but we can use "--root" instead
@ -98,7 +101,7 @@ _forgit_parse_array() {
${IFS+"false"} && unset old_IFS || old_IFS="$IFS" ${IFS+"false"} && unset old_IFS || old_IFS="$IFS"
# read the value of the second argument # read the value of the second argument
# into an array that has the name of the first argument # into an array that has the name of the first argument
IFS=" " read -r -a "$1" <<< "$2" IFS=" " read -r -a "$1" <<<"$2"
${old_IFS+"false"} && unset IFS || IFS="$old_IFS" ${old_IFS+"false"} && unset IFS || IFS="$old_IFS"
} }
@ -108,7 +111,7 @@ _forgit_quote_files() {
local files add local files add
files=() files=()
add=false add=false
while (( "$#" )); do while (("$#")); do
case "$1" in case "$1" in
--) --)
add=true add=true
@ -130,19 +133,19 @@ _forgit_log_format=${FORGIT_LOG_FORMAT:-%C(auto)%h%d %s %C(black)%C(bold)%cr%Cre
_forgit_log_preview_options=("--graph" "--pretty=format:$_forgit_log_format" "--color=always" "--abbrev-commit" "--date=relative") _forgit_log_preview_options=("--graph" "--pretty=format:$_forgit_log_format" "--color=always" "--abbrev-commit" "--date=relative")
_forgit_fullscreen_context=${FORGIT_FULLSCREEN_CONTEXT:-10} _forgit_fullscreen_context=${FORGIT_FULLSCREEN_CONTEXT:-10}
_forgit_preview_context=${FORGIT_PREVIEW_CONTEXT:-3} _forgit_preview_context=${FORGIT_PREVIEW_CONTEXT:-3}
_forgit_dir_view=${FORGIT_DIR_VIEW:-$(hash tree &> /dev/null && echo 'tree' || echo 'find')} _forgit_dir_view=${FORGIT_DIR_VIEW:-$(hash tree &>/dev/null && echo 'tree' || echo 'find')}
_forgit_pager() { _forgit_pager() {
local pager local pager
# Preview mode must be marked explicitly by forgit. Inferred signals such # Preview mode must be marked explicitly by forgit. Inferred signals such
# as FZF_PREVIEW_COLUMNS or a non-TTY stdout also show up in execute/fullscreen # as FZF_PREVIEW_COLUMNS or a non-TTY stdout also show up in execute/fullscreen
# paths, which would incorrectly route Enter actions to FORGIT_PREVIEW_PAGER. # paths, which would incorrectly route Enter actions to FORGIT_PREVIEW_PAGER.
if [[ -n "$FORGIT_IN_PREVIEW" ]] && [[ -n "$FORGIT_PREVIEW_PAGER" ]]; then if [[ -n $FORGIT_IN_PREVIEW ]] && [[ -n $FORGIT_PREVIEW_PAGER ]]; then
pager="$FORGIT_PREVIEW_PAGER" pager="$FORGIT_PREVIEW_PAGER"
else else
pager=$(_forgit_get_pager "$1") pager=$(_forgit_get_pager "$1")
fi fi
[[ -z "${pager}" ]] && exit 1 [[ -z ${pager} ]] && exit 1
eval "${pager} ${*:2}" eval "${pager} ${*:2}"
} }
@ -171,7 +174,7 @@ _forgit_get_pager() {
} }
_forgit_is_file_tracked() { _forgit_is_file_tracked() {
git ls-files "$1" --error-unmatch &> /dev/null git ls-files "$1" --error-unmatch &>/dev/null
} }
# List branches with current branch first (for use as fzf header) # List branches with current branch first (for use as fzf header)
@ -185,7 +188,7 @@ _forgit_is_file_tracked() {
_forgit_branch_list() { _forgit_branch_list() {
local current local current
current=$(git branch --show-current) current=$(git branch --show-current)
if [[ -n "$current" ]]; then if [[ -n $current ]]; then
_forgit_print_dim "* $current" _forgit_print_dim "* $current"
else else
_forgit_print_dim "* (HEAD detached at $(git rev-parse --short HEAD))" _forgit_print_dim "* (HEAD detached at $(git rev-parse --short HEAD))"
@ -361,7 +364,10 @@ _forgit_log() {
# git reflog viewer # git reflog viewer
_forgit_reflog() { _forgit_reflog() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
_forgit_contains_non_flags "$@" && { git reflog "$@"; return $?; } _forgit_contains_non_flags "$@" && {
git reflog "$@"
return $?
}
local opts reflog_format local opts reflog_format
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -426,7 +432,7 @@ _forgit_diff_view() {
repo=$(git rev-parse --show-toplevel) repo=$(git rev-parse --show-toplevel)
cd "$repo" || return 1 cd "$repo" || return 1
if [ $# -gt 2 ]; then if [ $# -gt 2 ]; then
IFS=" " read -r -a commits <<< "${*:3}" IFS=" " read -r -a commits <<<"${*:3}"
fi fi
echo "$input_line" | _forgit_get_files_from_diff_line | xargs -0 \ echo "$input_line" | _forgit_get_files_from_diff_line | xargs -0 \
"$FORGIT" exec_diff "${commits[@]}" -U"$diff_context" -- | _forgit_pager diff "$FORGIT" exec_diff "${commits[@]}" -U"$diff_context" -- | _forgit_pager diff
@ -453,7 +459,7 @@ _forgit_diff() {
commits=() commits=()
files=() files=()
[[ $# -ne 0 ]] && { [[ $# -ne 0 ]] && {
if git rev-parse "$1" -- &>/dev/null ; then if git rev-parse "$1" -- &>/dev/null; then
if [[ $# -gt 1 ]] && git rev-parse "$2" -- &>/dev/null; then if [[ $# -gt 1 ]] && git rev-parse "$2" -- &>/dev/null; then
commits=("$1" "$2") && files=("${@:3}") commits=("$1" "$2") && files=("${@:3}")
else else
@ -511,7 +517,7 @@ _forgit_show_preview() {
local input_line=$1 local input_line=$1
local diff_context=$2 local diff_context=$2
local commit=$3 local commit=$3
if [[ "$FZF_PREVIEW_LABEL" =~ "Diff" ]]; then if [[ $FZF_PREVIEW_LABEL =~ "Diff" ]]; then
_forgit_show_view "${input_line}" "${diff_context}" "${commit}" _forgit_show_view "${input_line}" "${diff_context}" "${commit}"
else else
git show --quiet --color=always "${FZF_PROMPT%% *}" git show --quiet --color=always "${FZF_PROMPT%% *}"
@ -543,7 +549,7 @@ _forgit_show() {
local files opts commit escaped_commit local files opts commit escaped_commit
files=() files=()
if [[ $# -ne 0 ]]; then if [[ $# -ne 0 ]]; then
if git rev-parse "$1" -- &>/dev/null ; then if git rev-parse "$1" -- &>/dev/null; then
commit="$1" && files=("${@:2}") commit="$1" && files=("${@:2}")
else else
commit="HEAD" && files=("$@") commit="HEAD" && files=("$@")
@ -577,7 +583,7 @@ _forgit_add_preview() {
file=$1 file=$1
# $file can be a directory when status.showUntrackedFiles is set to 'normal' # $file can be a directory when status.showUntrackedFiles is set to 'normal'
# When this is the case and the directory is not a submodule show the content of the directory and return # When this is the case and the directory is not a submodule show the content of the directory and return
if [[ -d "$file" ]] && ! _forgit_is_submodule "$file"; then if [[ -d $file ]] && ! _forgit_is_submodule "$file"; then
eval "$_forgit_dir_view \"$file\"" eval "$_forgit_dir_view \"$file\""
return 0 return 0
fi fi
@ -605,7 +611,10 @@ _forgit_add() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local files opts local files opts
# Add files if passed as arguments # Add files if passed as arguments
_forgit_contains_non_flags "$@" && { _forgit_git_add "$@" && git status -s; return $?; } _forgit_contains_non_flags "$@" && {
_forgit_git_add "$@" && git status -s
return $?
}
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -621,7 +630,7 @@ _forgit_add() {
while IFS='' read -r file; do while IFS='' read -r file; do
files+=("$file") files+=("$file")
done < <(_forgit_worktree_changes | FZF_DEFAULT_OPTS="$opts" fzf) done < <(_forgit_worktree_changes | FZF_DEFAULT_OPTS="$opts" fzf)
[[ "${#files[@]}" -gt 0 ]] && _forgit_git_add "$@" "${files[@]}" && git status -s && return [[ ${#files[@]} -gt 0 ]] && _forgit_git_add "$@" "${files[@]}" && git status -s && return
echo 'Nothing to add.' echo 'Nothing to add.'
} }
@ -640,7 +649,10 @@ _forgit_git_reset_head() {
_forgit_reset_head() { _forgit_reset_head() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local files opts rootdir local files opts rootdir
[[ $# -ne 0 ]] && { _forgit_git_reset_head "$@" && git status --short; return $?; } [[ $# -ne 0 ]] && {
_forgit_git_reset_head "$@" && git status --short
return $?
}
rootdir=$(git rev-parse --show-toplevel) rootdir=$(git rev-parse --show-toplevel)
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -678,7 +690,10 @@ _forgit_stash_show_enter() {
_forgit_stash_show() { _forgit_stash_show() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local opts local opts
[[ $# -ne 0 ]] && { "${FORGIT}" show "$@"; return $?; } [[ $# -ne 0 ]] && {
"${FORGIT}" show "$@"
return $?
}
_forgit_stash_show_git_opts=() _forgit_stash_show_git_opts=()
_forgit_parse_array _forgit_stash_show_git_opts "$FORGIT_STASH_SHOW_GIT_OPTS" _forgit_parse_array _forgit_stash_show_git_opts "$FORGIT_STASH_SHOW_GIT_OPTS"
opts=" opts="
@ -713,18 +728,21 @@ _forgit_git_stash_push() {
_forgit_stash_push() { _forgit_stash_push() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local msg args local msg args
args=( "$@" ) args=("$@")
while (( "$#" )); do while (("$#")); do
case "$1" in case "$1" in
# allow message as argument # allow message as argument
-m|--message) -m | --message)
msg="$2" msg="$2"
shift 2 shift 2
;; ;;
# ignore -u as it's used implicitly # ignore -u as it's used implicitly
-u|--include-untracked) shift ;; -u | --include-untracked) shift ;;
# pass to git directly when encountering anything else # pass to git directly when encountering anything else
*) _forgit_git_stash_push "${args[@]}"; return $? *)
_forgit_git_stash_push "${args[@]}"
return $?
;;
esac esac
done done
local opts files local opts files
@ -740,14 +758,14 @@ _forgit_stash_push() {
files+=("$file") files+=("$file")
done < <(_forgit_list_files --exclude-standard --modified --others | done < <(_forgit_list_files --exclude-standard --modified --others |
FZF_DEFAULT_OPTS="$opts" fzf --exit-0) FZF_DEFAULT_OPTS="$opts" fzf --exit-0)
[[ "${#files[@]}" -eq 0 ]] && echo "Nothing to stash" && return 1 [[ ${#files[@]} -eq 0 ]] && echo "Nothing to stash" && return 1
_forgit_git_stash_push ${msg:+-m "$msg"} -u "${files[@]}" _forgit_git_stash_push ${msg:+-m "$msg"} -u "${files[@]}"
} }
_forgit_clean_preview() { _forgit_clean_preview() {
local path local path
path=$1 path=$1
if [[ -d "$path" ]]; then if [[ -d $path ]]; then
eval "$_forgit_dir_view \"$path\"" eval "$_forgit_dir_view \"$path\""
else else
git diff --color=always /dev/null "$path" | _forgit_pager diff git diff --color=always /dev/null "$path" | _forgit_pager diff
@ -771,12 +789,15 @@ _forgit_clean_select_files() {
# git clean selector # git clean selector
_forgit_clean() { _forgit_clean() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
_forgit_contains_non_flags "$@" && { git clean -q "$@"; return $?; } _forgit_contains_non_flags "$@" && {
git clean -q "$@"
return $?
}
local files local files
_forgit_clean_git_opts=() _forgit_clean_git_opts=()
_forgit_parse_array _forgit_clean_git_opts "$FORGIT_CLEAN_GIT_OPTS" _forgit_parse_array _forgit_clean_git_opts "$FORGIT_CLEAN_GIT_OPTS"
files=$(_forgit_clean_select_files "$@") files=$(_forgit_clean_select_files "$@")
[[ -n "$files" ]] && echo "$files" | tr '\n' '\0' | xargs -0 -I% git clean "${_forgit_clean_git_opts[@]}" -xdff '%' && git status --short && return [[ -n $files ]] && echo "$files" | tr '\n' '\0' | xargs -0 -I% git clean "${_forgit_clean_git_opts[@]}" -xdff '%' && git status --short && return
echo 'Nothing to clean.' echo 'Nothing to clean.'
} }
@ -790,7 +811,7 @@ _forgit_cherry_pick() {
local base target opts fzf_selection fzf_exitval local base target opts fzf_selection fzf_exitval
base=$(git branch --show-current) base=$(git branch --show-current)
[[ -z "$base" ]] && echo "Current commit is not on a branch." && return 1 [[ -z $base ]] && echo "Current commit is not on a branch." && return 1
[[ -z $1 ]] && echo "Please specify target branch" && return 1 [[ -z $1 ]] && echo "Please specify target branch" && return 1
target="$1" target="$1"
@ -812,7 +833,7 @@ _forgit_cherry_pick() {
FZF_DEFAULT_OPTS="$opts" fzf) FZF_DEFAULT_OPTS="$opts" fzf)
fzf_exitval=$? fzf_exitval=$?
[[ $fzf_exitval != 0 ]] && return $fzf_exitval [[ $fzf_exitval != 0 ]] && return $fzf_exitval
[[ -z "$fzf_selection" ]] && return $fzf_exitval [[ -z $fzf_selection ]] && return $fzf_exitval
commits=() commits=()
while IFS='' read -r commit; do while IFS='' read -r commit; do
@ -836,7 +857,7 @@ _forgit_cherry_pick_from_branch() {
local opts branch exitval input_branch args base local opts branch exitval input_branch args base
base=$(git branch --show-current) base=$(git branch --show-current)
[[ -z "$base" ]] && echo "Current commit is not on a branch." && return 1 [[ -z $base ]] && echo "Current commit is not on a branch." && return 1
args=("$@") args=("$@")
if [[ $# -ne 0 ]]; then if [[ $# -ne 0 ]]; then
@ -850,8 +871,7 @@ _forgit_cherry_pick_from_branch() {
" "
# loop until either the branch selector is closed or a commit to be cherry # loop until either the branch selector is closed or a commit to be cherry
# picked has been selected from within a branch # picked has been selected from within a branch
while true while true; do
do
if [[ -z $input_branch ]]; then if [[ -z $input_branch ]]; then
branch="$(_forgit_branch_list --all | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)" branch="$(_forgit_branch_list --all | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)"
else else
@ -859,7 +879,7 @@ _forgit_cherry_pick_from_branch() {
fi fi
unset input_branch unset input_branch
[[ -z "$branch" ]] && return 1 [[ -z $branch ]] && return 1
_forgit_cherry_pick "$branch" _forgit_cherry_pick "$branch"
@ -870,7 +890,10 @@ _forgit_cherry_pick_from_branch() {
_forgit_rebase() { _forgit_rebase() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
_forgit_contains_non_flags "$@" && { git rebase "$@"; return $?; } _forgit_contains_non_flags "$@" && {
git rebase "$@"
return $?
}
local opts graph target_commit prev_commit local opts graph target_commit prev_commit
graph=() graph=()
[[ $_forgit_log_graph_enable == true ]] && graph=(--graph) [[ $_forgit_log_graph_enable == true ]] && graph=(--graph)
@ -887,8 +910,9 @@ _forgit_rebase() {
git log "${graph[@]}" --color=always --format="$_forgit_log_format" | git log "${graph[@]}" --color=always --format="$_forgit_log_format" |
_forgit_emojify | _forgit_emojify |
FZF_DEFAULT_OPTS="$opts" fzf | FZF_DEFAULT_OPTS="$opts" fzf |
_forgit_extract_sha) _forgit_extract_sha
if [[ -n "$target_commit" ]]; then )
if [[ -n $target_commit ]]; then
prev_commit=$(_forgit_previous_commit "$target_commit") prev_commit=$(_forgit_previous_commit "$target_commit")
git rebase -i "${_forgit_rebase_git_opts[@]}" "$@" "$prev_commit" git rebase -i "${_forgit_rebase_git_opts[@]}" "$@" "$prev_commit"
fi fi
@ -934,9 +958,10 @@ _forgit_edit_commit() {
git log "${graph[@]}" --color=always --format="$_forgit_log_format" "$@" | git log "${graph[@]}" --color=always --format="$_forgit_log_format" "$@" |
_forgit_emojify | _forgit_emojify |
FZF_DEFAULT_OPTS="$opts" fzf | FZF_DEFAULT_OPTS="$opts" fzf |
_forgit_extract_sha) _forgit_extract_sha
)
# GIT_EDITOR=: is needed to skip the editor # GIT_EDITOR=: is needed to skip the editor
if [[ -n "$target_commit" ]] && GIT_EDITOR=: git commit "${git_opts[@]}" "$action" "$target_commit"; then if [[ -n $target_commit ]] && GIT_EDITOR=: git commit "${git_opts[@]}" "$action" "$target_commit"; then
prev_commit=$(_forgit_previous_commit "$target_commit") prev_commit=$(_forgit_previous_commit "$target_commit")
# rebase will fail if there are unstaged changes so --autostash is needed to temporarily stash them # 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=: is needed to skip the editor
@ -963,8 +988,9 @@ _forgit_reword() {
git log "${graph[@]}" --color=always --format="$_forgit_log_format" "$@" | git log "${graph[@]}" --color=always --format="$_forgit_log_format" "$@" |
_forgit_emojify | _forgit_emojify |
FZF_DEFAULT_OPTS="$opts" fzf | FZF_DEFAULT_OPTS="$opts" fzf |
_forgit_extract_sha) _forgit_extract_sha
if [[ -n "$target_commit" ]] && git commit "${git_opts[@]}" --fixup=reword:"$target_commit"; then )
if [[ -n $target_commit ]] && git commit "${git_opts[@]}" --fixup=reword:"$target_commit"; then
prev_commit=$(_forgit_previous_commit "$target_commit") prev_commit=$(_forgit_previous_commit "$target_commit")
# rebase will fail if there are unstaged changes so --autostash is needed to temporarily stash them # 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=: is needed to skip the editor
@ -986,7 +1012,10 @@ _forgit_git_checkout_file() {
_forgit_checkout_file() { _forgit_checkout_file() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local files opts local files opts
_forgit_contains_non_flags "$@" && { _forgit_git_checkout_file "$@"; return $?; } _forgit_contains_non_flags "$@" && {
_forgit_git_checkout_file "$@"
return $?
}
[[ $(_forgit_list_files --modified | wc -l) -eq 0 ]] && echo 'Nothing to checkout.' && return 1 [[ $(_forgit_list_files --modified | wc -l) -eq 0 ]] && echo 'Nothing to checkout.' && return 1
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -999,7 +1028,7 @@ _forgit_checkout_file() {
files+=("$file") files+=("$file")
done < <(_forgit_list_files --modified | done < <(_forgit_list_files --modified |
FZF_DEFAULT_OPTS="$opts" fzf) FZF_DEFAULT_OPTS="$opts" fzf)
[[ "${#files[@]}" -gt 0 ]] && _forgit_git_checkout_file "$@" "${files[@]}" [[ ${#files[@]} -gt 0 ]] && _forgit_git_checkout_file "$@" "${files[@]}"
} }
# git checkout-file from commit selector # git checkout-file from commit selector
@ -1025,7 +1054,7 @@ _forgit_checkout_file_from_commit() {
commit=$(_forgit_git_log "$_forgit_log_format" "$branch" "$@" | commit=$(_forgit_git_log "$_forgit_log_format" "$branch" "$@" |
FZF_DEFAULT_OPTS="$opts" fzf | FZF_DEFAULT_OPTS="$opts" fzf |
_forgit_extract_sha) _forgit_extract_sha)
[[ -n "$commit" ]] || return 0 [[ -n $commit ]] || return 0
# select the file interactively # select the file interactively
opts=" opts="
@ -1040,11 +1069,11 @@ _forgit_checkout_file_from_commit() {
--prompt=\"${commit} > \" --prompt=\"${commit} > \"
" "
file=$(_forgit_git_show "$commit" | FZF_DEFAULT_OPTS="$opts" fzf) file=$(_forgit_git_show "$commit" | FZF_DEFAULT_OPTS="$opts" fzf)
[[ -n "$file" ]] || return 0 [[ -n $file ]] || return 0
# special case: when the file was deleted in the commit # special case: when the file was deleted in the commit
# check out the file from the previous commit. # check out the file from the previous commit.
[[ "$file" =~ ^\[D\] ]] && commit="$commit~" [[ $file =~ ^\[D\] ]] && commit="$commit~"
file=$(echo "$file" | _forgit_get_single_file_from_diff_line) file=$(echo "$file" | _forgit_get_single_file_from_diff_line)
_forgit_git_checkout_file "$commit" -- "$file" _forgit_git_checkout_file "$commit" -- "$file"
@ -1061,7 +1090,7 @@ _forgit_checkout_branch() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
# if called with arguments, check if branch exists, else create a new one # if called with arguments, check if branch exists, else create a new one
if [[ $# -ne 0 ]]; then if [[ $# -ne 0 ]]; then
if [[ "$*" == "-" ]] || git show-branch "$@" &>/dev/null; then if [[ $* == "-" ]] || git show-branch "$@" &>/dev/null; then
git switch "$@" git switch "$@"
else else
git switch -c "$@" git switch -c "$@"
@ -1082,10 +1111,10 @@ _forgit_checkout_branch() {
_forgit_parse_array _forgit_checkout_branch_branch_git_opts "$FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS" _forgit_parse_array _forgit_checkout_branch_branch_git_opts "$FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS"
branch="$(_forgit_branch_list "${_forgit_checkout_branch_branch_git_opts[@]:---all}" | branch="$(_forgit_branch_list "${_forgit_checkout_branch_branch_git_opts[@]:---all}" |
FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)" FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)"
[[ -z "$branch" ]] && return 1 [[ -z $branch ]] && return 1
# track the remote branch if possible # track the remote branch if possible
if [[ "$branch" == "remotes/"* ]]; then if [[ $branch == "remotes/"* ]]; then
if git branch | grep -qw "${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 # 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" _forgit_git_checkout_branch -b "track/${branch#remotes/*/}" --track "$branch"
@ -1107,7 +1136,7 @@ _forgit_switch_branch() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
# if called with arguments, check if branch exists, else create a new one # if called with arguments, check if branch exists, else create a new one
if [[ $# -ne 0 ]]; then if [[ $# -ne 0 ]]; then
if [[ "$*" == "-" ]] || git show-branch "$@" &>/dev/null; then if [[ $* == "-" ]] || git show-branch "$@" &>/dev/null; then
git switch "$@" git switch "$@"
else else
git switch -c "$@" git switch -c "$@"
@ -1128,10 +1157,10 @@ _forgit_switch_branch() {
_forgit_parse_array _forgit_switch_branch_branch_git_opts "$FORGIT_SWITCH_BRANCH_BRANCH_GIT_OPTS" _forgit_parse_array _forgit_switch_branch_branch_git_opts "$FORGIT_SWITCH_BRANCH_BRANCH_GIT_OPTS"
branch="$(_forgit_branch_list "${_forgit_switch_branch_branch_git_opts[@]:---all}" | branch="$(_forgit_branch_list "${_forgit_switch_branch_branch_git_opts[@]:---all}" |
FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)" FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)"
[[ -z "$branch" ]] && return 1 [[ -z $branch ]] && return 1
# track the remote branch if possible # track the remote branch if possible
if [[ "$branch" == "remotes/"* ]]; then if [[ $branch == "remotes/"* ]]; then
if git branch | grep -qw "${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 # hack to force creating a new branch which tracks the remote if a local branch already exists
_forgit_git_switch_branch --create "track/${branch#remotes/*/}" --track "$branch" _forgit_git_switch_branch --create "track/${branch#remotes/*/}" --track "$branch"
@ -1153,7 +1182,10 @@ _forgit_git_checkout_tag() {
_forgit_checkout_tag() { _forgit_checkout_tag() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local opts tag local opts tag
[[ $# -ne 0 ]] && { _forgit_git_checkout_tag "$@"; return $?; } [[ $# -ne 0 ]] && {
_forgit_git_checkout_tag "$@"
return $?
}
[[ $(git tag -l | wc -l) -eq 0 ]] && echo 'Nothing to checkout: there are no tags.' && return 1 [[ $(git tag -l | wc -l) -eq 0 ]] && echo 'Nothing to checkout: there are no tags.' && return 1
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -1162,7 +1194,7 @@ _forgit_checkout_tag() {
$FORGIT_CHECKOUT_TAG_FZF_OPTS $FORGIT_CHECKOUT_TAG_FZF_OPTS
" "
tag="$(git tag -l --sort=-v:refname | FZF_DEFAULT_OPTS="$opts" fzf)" tag="$(git tag -l --sort=-v:refname | FZF_DEFAULT_OPTS="$opts" fzf)"
[[ -z "$tag" ]] && return 1 [[ -z $tag ]] && return 1
_forgit_git_checkout_tag "$tag" _forgit_git_checkout_tag "$tag"
} }
@ -1182,7 +1214,10 @@ _forgit_git_checkout_commit() {
_forgit_checkout_commit() { _forgit_checkout_commit() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local opts graph commit local opts graph commit
[[ $# -ne 0 ]] && { _forgit_git_checkout_commit "$@"; return $?; } [[ $# -ne 0 ]] && {
_forgit_git_checkout_commit "$@"
return $?
}
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
+s +m --tiebreak=index +s +m --tiebreak=index
@ -1215,7 +1250,10 @@ _forgit_git_branch_delete() {
_forgit_branch_delete() { _forgit_branch_delete() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local opts local opts
[[ $# -ne 0 ]] && { _forgit_git_branch_delete "$@"; return $?; } [[ $# -ne 0 ]] && {
_forgit_git_branch_delete "$@"
return $?
}
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -1223,8 +1261,7 @@ _forgit_branch_delete() {
--preview=\"$FORGIT preview branch_preview {}\" --preview=\"$FORGIT preview branch_preview {}\"
$FORGIT_BRANCH_DELETE_FZF_OPTS $FORGIT_BRANCH_DELETE_FZF_OPTS
" "
for branch in $(_forgit_branch_list | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name) for branch in $(_forgit_branch_list | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name); do
do
_forgit_git_branch_delete "$branch" _forgit_git_branch_delete "$branch"
done done
} }
@ -1245,7 +1282,10 @@ _forgit_git_revert() {
_forgit_revert_commit() { _forgit_revert_commit() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local opts commits IFS local opts commits IFS
[[ $# -ne 0 ]] && { _forgit_git_revert "$@"; return $?; } [[ $# -ne 0 ]] && {
_forgit_git_revert "$@"
return $?
}
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -1272,7 +1312,8 @@ _forgit_revert_commit() {
FZF_DEFAULT_OPTS="$opts" fzf | FZF_DEFAULT_OPTS="$opts" fzf |
sort -n -k 1 | sort -n -k 1 |
cut -f2- | cut -f2- |
sed 's/^[^a-f^0-9]*\([a-f0-9]*\).*/\1/') sed 's/^[^a-f^0-9]*\([a-f0-9]*\).*/\1/'
)
[ ${#commits[@]} -eq 0 ] && return 1 [ ${#commits[@]} -eq 0 ] && return 1
@ -1299,7 +1340,10 @@ _forgit_git_blame() {
_forgit_blame() { _forgit_blame() {
_forgit_inside_work_tree || return 1 _forgit_inside_work_tree || return 1
local opts flags file local opts flags file
_forgit_contains_non_flags "$@" && { _forgit_git_blame "$@"; return $?; } _forgit_contains_non_flags "$@" && {
_forgit_git_blame "$@"
return $?
}
flags=() flags=()
while IFS='' read -r flag; do while IFS='' read -r flag; do
flags+=("$flag") flags+=("$flag")
@ -1312,7 +1356,7 @@ _forgit_blame() {
# flags is not quoted here, which is fine given that they are retrieved # flags is not quoted here, which is fine given that they are retrieved
# with git rev-parse and can only contain flags # with git rev-parse and can only contain flags
file=$(FZF_DEFAULT_OPTS="$opts" fzf) file=$(FZF_DEFAULT_OPTS="$opts" fzf)
[[ -z "$file" ]] && return 1 [[ -z $file ]] && return 1
_forgit_git_blame "$file" "${flags[@]}" _forgit_git_blame "$file" "${flags[@]}"
} }
@ -1335,8 +1379,8 @@ _forgit_path_preview() {
} }
_forgit_ignore() { _forgit_ignore() {
[ -d "$FORGIT_GI_REPO_LOCAL" ] \ [ -d "$FORGIT_GI_REPO_LOCAL" ] ||
|| _forgit_repo_update "$FORGIT_GI_REPO_REMOTE" "$FORGIT_GI_REPO_LOCAL" _forgit_repo_update "$FORGIT_GI_REPO_REMOTE" "$FORGIT_GI_REPO_LOCAL"
local IFS args opts local IFS args opts
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -1363,8 +1407,8 @@ export FORGIT_ATTR_REPO_LOCAL=${FORGIT_ATTR_REPO_LOCAL:-${XDG_CACHE_HOME:-$HOME/
export FORGIT_ATTR_TEMPLATES=${FORGIT_ATTR_TEMPLATES:-$FORGIT_ATTR_REPO_LOCAL} export FORGIT_ATTR_TEMPLATES=${FORGIT_ATTR_TEMPLATES:-$FORGIT_ATTR_REPO_LOCAL}
_forgit_attributes() { _forgit_attributes() {
[ -d "$FORGIT_ATTR_REPO_LOCAL" ] \ [ -d "$FORGIT_ATTR_REPO_LOCAL" ] ||
|| _forgit_repo_update "$FORGIT_ATTR_REPO_REMOTE" "$FORGIT_ATTR_REPO_LOCAL" _forgit_repo_update "$FORGIT_ATTR_REPO_REMOTE" "$FORGIT_ATTR_REPO_LOCAL"
local IFS args opts local IFS args opts
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -1389,7 +1433,7 @@ _forgit_repo_update() {
local remote path local remote path
remote=$1 remote=$1
path=$2 path=$2
if [[ -d "$path" ]]; then if [[ -d $path ]]; then
_forgit_info 'Updating repo...' _forgit_info 'Updating repo...'
(cd "$path" && git pull --no-rebase --ff) || return 1 (cd "$path" && git pull --no-rebase --ff) || return 1
else else
@ -1405,7 +1449,7 @@ _forgit_path_get() {
shift 2 shift 2
for item in "$@"; do for item in "$@"; do
if filename=$(find -L "$path" -type f \( -iname "${item}$ext" -o -iname "${item}" \) -print -quit); then 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 [[ -z $filename ]] && _forgit_warn "No template found for '$item'." && continue
header="${filename##*/}" && header="${header%"$ext"}" header="${filename##*/}" && header="${header%"$ext"}"
echo "### $header" && cat "$filename" && echo echo "### $header" && cat "$filename" && echo
fi fi
@ -1416,7 +1460,7 @@ _forgit_paths_list() {
local path ext local path ext
path=$1 path=$1
ext=$2 ext=$2
find "$path" -name "*$ext" -print |sed -e "s#$ext\$##" -e 's#.*/##' -e '/^$/d' | sort -fu find "$path" -name "*$ext" -print | sed -e "s#$ext\$##" -e 's#.*/##' -e '/^$/d' | sort -fu
} }
# Get the root path of the main worktree (not the current worktree) # Get the root path of the main worktree (not the current worktree)
@ -1458,9 +1502,9 @@ _forgit_worktree_list() {
"") "")
relative_date=$(git log -1 --format='%cr' "$head" 2>/dev/null) relative_date=$(git log -1 --format='%cr' "$head" 2>/dev/null)
local current_marker=" " lock_marker=" " local current_marker=" " lock_marker=" "
[[ "$worktree" == "$current_worktree" ]] && current_marker="*" [[ $worktree == "$current_worktree" ]] && current_marker="*"
[[ -n "$prunable" ]] && lock_marker="$prunable" [[ -n $prunable ]] && lock_marker="$prunable"
[[ -n "$locked" ]] && lock_marker="$locked" [[ -n $locked ]] && lock_marker="$locked"
printf "[%s%s] %s ${_cyan}(%s)${_reset} ${_gray}%s${_reset}\n" \ printf "[%s%s] %s ${_cyan}(%s)${_reset} ${_gray}%s${_reset}\n" \
"$current_marker" "$lock_marker" "$worktree" "${branch:-HEAD}" "$relative_date" "$current_marker" "$lock_marker" "$worktree" "${branch:-HEAD}" "$relative_date"
;; ;;
@ -1491,7 +1535,7 @@ _forgit_worktree_toggle_lock() {
local line="$1" worktree stripped local line="$1" worktree stripped
worktree=$(echo "$line" | _forgit_extract_worktree_path) worktree=$(echo "$line" | _forgit_extract_worktree_path)
stripped=$(echo "$line" | _forgit_strip_ansi) stripped=$(echo "$line" | _forgit_strip_ansi)
if [[ "${stripped:2:1}" == "L" ]]; then if [[ ${stripped:2:1} == "L" ]]; then
git worktree unlock "$worktree" git worktree unlock "$worktree"
else else
git worktree lock "$worktree" git worktree lock "$worktree"
@ -1502,11 +1546,11 @@ _forgit_worktree_toggle_lock() {
_forgit_worktree_preview() { _forgit_worktree_preview() {
local worktree local worktree
worktree=$(echo "$1" | _forgit_extract_worktree_path) worktree=$(echo "$1" | _forgit_extract_worktree_path)
[[ ! -d "$worktree" ]] && echo "Worktree directory not found: $worktree" && return 1 [[ ! -d $worktree ]] && echo "Worktree directory not found: $worktree" && return 1
local status_output local status_output
status_output=$(git -c color.status=always -C "$worktree" status -s 2>/dev/null) status_output=$(git -c color.status=always -C "$worktree" status -s 2>/dev/null)
[[ -n "$status_output" ]] && echo "$status_output" && echo "" [[ -n $status_output ]] && echo "$status_output" && echo ""
git -C "$worktree" log --oneline -n 200 --color=always 2>/dev/null git -C "$worktree" log --oneline -n 200 --color=always 2>/dev/null
} }
@ -1523,7 +1567,10 @@ _forgit_git_worktree_delete() {
_forgit_worktree() { _forgit_worktree() {
_forgit_inside_git_repo || return 1 _forgit_inside_git_repo || return 1
local opts worktree local opts worktree
[[ $# -ne 0 ]] && { git worktree "$@"; return $?; } [[ $# -ne 0 ]] && {
git worktree "$@"
return $?
}
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -1534,7 +1581,7 @@ _forgit_worktree() {
$FORGIT_WORKTREE_FZF_OPTS $FORGIT_WORKTREE_FZF_OPTS
" "
worktree=$(_forgit_worktree_list | FZF_DEFAULT_OPTS="$opts" fzf) worktree=$(_forgit_worktree_list | FZF_DEFAULT_OPTS="$opts" fzf)
[[ -z "$worktree" ]] && return 1 [[ -z $worktree ]] && return 1
echo "$worktree" | _forgit_extract_worktree_path echo "$worktree" | _forgit_extract_worktree_path
} }
@ -1542,11 +1589,14 @@ _forgit_worktree() {
_forgit_worktree_delete() { _forgit_worktree_delete() {
_forgit_inside_git_repo || return 1 _forgit_inside_git_repo || return 1
local opts worktrees local opts worktrees
[[ $# -ne 0 ]] && { _forgit_git_worktree_delete "$@"; return $?; } [[ $# -ne 0 ]] && {
_forgit_git_worktree_delete "$@"
return $?
}
local candidates local candidates
candidates=$(_forgit_worktree_list_deletable) candidates=$(_forgit_worktree_list_deletable)
if [[ -z "$candidates" ]]; then if [[ -z $candidates ]]; then
echo "Nothing to delete." echo "Nothing to delete."
return 1 return 1
fi fi
@ -1562,7 +1612,7 @@ _forgit_worktree_delete() {
worktrees=() worktrees=()
while IFS='' read -r line; do while IFS='' read -r line; do
[[ -n "$line" ]] && worktrees+=("$(echo "$line" | _forgit_extract_worktree_path)") [[ -n $line ]] && worktrees+=("$(echo "$line" | _forgit_extract_worktree_path)")
done < <(echo "$candidates" | FZF_DEFAULT_OPTS="$opts" fzf) done < <(echo "$candidates" | FZF_DEFAULT_OPTS="$opts" fzf)
[[ ${#worktrees[@]} -eq 0 ]] && return 1 [[ ${#worktrees[@]} -eq 0 ]] && return 1
@ -1591,7 +1641,7 @@ _forgit_worktree_add() {
_forgit_parse_array _forgit_worktree_add_branch_git_opts "$FORGIT_WORKTREE_ADD_BRANCH_GIT_OPTS" _forgit_parse_array _forgit_worktree_add_branch_git_opts "$FORGIT_WORKTREE_ADD_BRANCH_GIT_OPTS"
local header_opt="" local header_opt=""
[[ -z "$new_branch" ]] && header_opt="--header-lines=1" [[ -z $new_branch ]] && header_opt="--header-lines=1"
opts=" opts="
$FORGIT_FZF_DEFAULT_OPTS $FORGIT_FZF_DEFAULT_OPTS
@ -1601,12 +1651,12 @@ _forgit_worktree_add() {
" "
branch=$(_forgit_branch_list "${_forgit_worktree_add_branch_git_opts[@]:---all}" | branch=$(_forgit_branch_list "${_forgit_worktree_add_branch_git_opts[@]:---all}" |
FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name) FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)
[[ -z "$branch" ]] && return 1 [[ -z $branch ]] && return 1
# Strip remotes/<remote>/ prefix for remote branches # Strip remotes/<remote>/ prefix for remote branches
local local_branch="${branch#remotes/*/}" local local_branch="${branch#remotes/*/}"
if [[ -n "$new_branch" ]]; then if [[ -n $new_branch ]]; then
# gwa <new-branch>: create new branch from selected base # gwa <new-branch>: create new branch from selected base
local target="$wt_dir/$new_branch" local target="$wt_dir/$new_branch"
git worktree add -b "$new_branch" "$target" "$branch" >&2 && echo "$target" git worktree add -b "$new_branch" "$target" "$branch" >&2 && echo "$target"
@ -1623,14 +1673,14 @@ check_prequisites() {
# Check if fzf is installed # Check if fzf is installed
installed_fzf_version=$(fzf --version 2>/dev/null | awk '{print $1}') installed_fzf_version=$(fzf --version 2>/dev/null | awk '{print $1}')
if [[ -z "$installed_fzf_version" ]]; then if [[ -z $installed_fzf_version ]]; then
echo "fzf is not installed. Please install fzf first." echo "fzf is not installed. Please install fzf first."
exit 1 exit 1
fi fi
# Check fzf version # Check fzf version
higher_fzf_version=$(printf '%s\n' "$REQUIRED_FZF_VERSION" "$installed_fzf_version" | sort -V | tail -n1) higher_fzf_version=$(printf '%s\n' "$REQUIRED_FZF_VERSION" "$installed_fzf_version" | sort -V | tail -n1)
if [[ "$higher_fzf_version" != "$installed_fzf_version" ]]; then if [[ $higher_fzf_version != "$installed_fzf_version" ]]; then
echo "fzf version $REQUIRED_FZF_VERSION or higher is required. You have $installed_fzf_version." echo "fzf version $REQUIRED_FZF_VERSION or higher is required. You have $installed_fzf_version."
exit 1 exit 1
fi fi
@ -1651,7 +1701,7 @@ main() {
# shellcheck disable=SC2076 # shellcheck disable=SC2076
if [[ ! " ${PUBLIC_COMMANDS[*]} " =~ " ${cmd} " ]] && [[ ! " ${PRIVATE_COMMANDS[*]} " =~ " ${cmd} " ]]; then if [[ ! " ${PUBLIC_COMMANDS[*]} " =~ " ${cmd} " ]] && [[ ! " ${PRIVATE_COMMANDS[*]} " =~ " ${cmd} " ]]; then
if [[ -z "$cmd" ]]; then if [[ -z $cmd ]]; then
printf "forgit: missing command\n\n" printf "forgit: missing command\n\n"
else else
printf "forgit: '%s' is not a valid forgit command.\n\n" "$cmd" printf "forgit: '%s' is not a valid forgit command.\n\n" "$cmd"
@ -1718,7 +1768,7 @@ PRIVATE_COMMANDS=(
# Check if the script is being sourced. This is necessary for unit tests where # Check if the script is being sourced. This is necessary for unit tests where
# we do not want to execute the main function. # we do not want to execute the main function.
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then if [[ ${BASH_SOURCE[0]} != "$0" ]]; then
return 0 return 0
fi fi

View file

@ -10,36 +10,30 @@
# When using forgit via the shell plugin, source this file explicitly after # When using forgit via the shell plugin, source this file explicitly after
# forgit.plugin.zsh to enable tab completion for shell functions and aliases. # forgit.plugin.zsh to enable tab completion for shell functions and aliases.
_git_branch_delete() _git_branch_delete() {
{
__gitcomp_nl "$(__git_heads)" __gitcomp_nl "$(__git_heads)"
} }
_git_checkout_branch() _git_checkout_branch() {
{
__gitcomp_nl "$(__git branch -a --format '%(refname:short)')" __gitcomp_nl "$(__git branch -a --format '%(refname:short)')"
} }
_git_checkout_file() _git_checkout_file() {
{
__gitcomp_nl "$(__git ls-files --modified)" __gitcomp_nl "$(__git ls-files --modified)"
} }
_git_checkout_tag() _git_checkout_tag() {
{
__gitcomp_nl "$(__git_tags)" __gitcomp_nl "$(__git_tags)"
} }
_git_stash_show() _git_stash_show() {
{
__gitcomp_nl "$(__git stash list | sed -n -e 's/:.*//p')" __gitcomp_nl "$(__git stash list | sed -n -e 's/:.*//p')"
} }
_git_worktrees() _git_worktrees() {
{
local wt_list="" field local wt_list="" field
while IFS= read -r -d '' field; do while IFS= read -r -d '' field; do
[[ "$field" == worktree\ * ]] && wt_list+="${field#worktree }"$'\n' [[ $field == worktree\ * ]] && wt_list+="${field#worktree }"$'\n'
done < <(__git worktree list --porcelain -z 2>/dev/null) done < <(__git worktree list --porcelain -z 2>/dev/null)
__gitcomp_nl "$wt_list" __gitcomp_nl "$wt_list"
} }
@ -47,20 +41,18 @@ _git_worktrees()
# Completion for git-forgit # Completion for git-forgit
# This includes git aliases, e.g. "alias.cb=forgit checkout_branch" will # This includes git aliases, e.g. "alias.cb=forgit checkout_branch" will
# correctly complete available branches on "git cb". # correctly complete available branches on "git cb".
_git_forgit() _git_forgit() {
{
local subcommand cword cur prev cmds local subcommand cword cur prev cmds
subcommand="${COMP_WORDS[1]}" subcommand="${COMP_WORDS[1]}"
if [[ "$subcommand" != "forgit" ]] if [[ $subcommand != "forgit" ]]; then
then
# Forgit is obviously called via a git alias. Get the original # Forgit is obviously called via a git alias. Get the original
# aliased subcommand and proceed as if it was the previous word. # aliased subcommand and proceed as if it was the previous word.
prev=$(git config --get "alias.$subcommand" | cut -d' ' -f 2) prev=$(git config --get "alias.$subcommand" | cut -d' ' -f 2)
cword=$((${COMP_CWORD} + 1)) cword=$((COMP_CWORD + 1))
else else
cword=${COMP_CWORD} cword=${COMP_CWORD}
prev=${COMP_WORDS[COMP_CWORD-1]} prev=${COMP_WORDS[COMP_CWORD - 1]}
fi fi
cur=${COMP_WORDS[COMP_CWORD]} cur=${COMP_WORDS[COMP_CWORD]}
@ -140,8 +132,7 @@ _git_forgit()
} }
# Check if forgit plugin is loaded # Check if forgit plugin is loaded
if [[ $(type -t forgit::add) == function ]] if [[ $(type -t forgit::add) == function ]]; then
then
# We're reusing existing git completion functions, so load those first # We're reusing existing git completion functions, so load those first
# and check if completion function exists afterwards. # and check if completion function exists afterwards.
_completion_loader git _completion_loader git
@ -175,7 +166,7 @@ then
__git_complete forgit::worktree::delete _git_worktrees __git_complete forgit::worktree::delete _git_worktrees
# Completion for forgit plugin shell aliases # Completion for forgit plugin shell aliases
if [[ -z "$FORGIT_NO_ALIASES" ]]; then if [[ -z $FORGIT_NO_ALIASES ]]; then
__git_complete "${forgit_add}" _git_add __git_complete "${forgit_add}" _git_add
__git_complete "${forgit_branch_delete}" _git_branch_delete __git_complete "${forgit_branch_delete}" _git_branch_delete
__git_complete "${forgit_checkout_branch}" _git_checkout_branch __git_complete "${forgit_checkout_branch}" _git_checkout_branch

View file

@ -5,17 +5,20 @@
# These shell built-ins prevent the wrong commands getting executed in case a # These shell built-ins prevent the wrong commands getting executed in case a
# user added a shell alias with the same name. # user added a shell alias with the same name.
forgit::error() { command printf "%b[Error]%b %s\n" '\e[0;31m' '\e[0m' "$@" >&2; builtin return 1; } forgit::error() {
command printf "%b[Error]%b %s\n" '\e[0;31m' '\e[0m' "$@" >&2
builtin return 1
}
forgit::warn() { command printf "%b[Warn]%b %s\n" '\e[0;33m' '\e[0m' "$@" >&2; } forgit::warn() { command printf "%b[Warn]%b %s\n" '\e[0;33m' '\e[0m' "$@" >&2; }
# determine installation path # determine installation path
if [[ -n "$ZSH_VERSION" ]]; then if [[ -n $ZSH_VERSION ]]; then
# shellcheck disable=2277,2296,2299 # shellcheck disable=2277,2296,2299
0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}" 0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}"
# shellcheck disable=2277,2296,2298 # shellcheck disable=2277,2296,2298
0="${${(M)0:#/*}:-$PWD/$0}" 0="${${(M)0:#/*}:-$PWD/$0}"
FORGIT_INSTALL_DIR="${0:h}" FORGIT_INSTALL_DIR="${0:h}"
elif [[ -n "$BASH_VERSION" ]]; then elif [[ -n $BASH_VERSION ]]; then
FORGIT_INSTALL_DIR="$(command dirname -- "${BASH_SOURCE[0]}")" FORGIT_INSTALL_DIR="$(command dirname -- "${BASH_SOURCE[0]}")"
else else
forgit::error "Only zsh and bash are supported" forgit::error "Only zsh and bash are supported"
@ -28,9 +31,10 @@ FORGIT="$FORGIT_INSTALL_DIR/bin/git-forgit"
# export all user-defined FORGIT variables to make them available in git-forgit # export all user-defined FORGIT variables to make them available in git-forgit
unexported_vars=0 unexported_vars=0
# Set posix mode in bash to only get variables, see #256. # Set posix mode in bash to only get variables, see #256.
[[ -n "$BASH_VERSION" ]] && builtin set -o posix [[ -n $BASH_VERSION ]] && builtin set -o posix
builtin set | command awk -F '=' '{ print $1 }' | command grep FORGIT_ | while builtin read -r var; do builtin set | command awk -F '=' '{ print $1 }' | command grep FORGIT_ | while builtin read -r var; do
if ! builtin export | command grep -q "\(^$var=\|^export $var=\)"; then if [[ $unexported_vars == 0 ]]; then if ! builtin export | command grep -q "\(^$var=\|^export $var=\)"; then
if [[ $unexported_vars == 0 ]]; then
forgit::warn "Config options have to be exported in future versions of forgit." forgit::warn "Config options have to be exported in future versions of forgit."
forgit::warn "Please update your config accordingly:" forgit::warn "Please update your config accordingly:"
fi fi
@ -41,7 +45,7 @@ builtin set | command awk -F '=' '{ print $1 }' | command grep FORGIT_ | while b
fi fi
done done
builtin unset unexported_vars builtin unset unexported_vars
[[ -n "$BASH_VERSION" ]] && builtin set +o posix [[ -n $BASH_VERSION ]] && builtin set +o posix
# register shell functions # register shell functions
forgit::log() { forgit::log() {
@ -165,16 +169,19 @@ forgit::attributes() {
} }
forgit::worktree() { forgit::worktree() {
if [[ $# -ne 0 ]]; then "$FORGIT" worktree "$@"; return $?; fi if [[ $# -ne 0 ]]; then
"$FORGIT" worktree "$@"
return $?
fi
local tree local tree
tree=$("$FORGIT" worktree) || return $? tree=$("$FORGIT" worktree) || return $?
[[ -d "$tree" ]] && builtin cd "$tree" || return 1 [[ -d $tree ]] && builtin cd "$tree" || return 1
} }
forgit::worktree::add() { forgit::worktree::add() {
local tree local tree
tree=$("$FORGIT" worktree_add "$@") || return $? tree=$("$FORGIT" worktree_add "$@") || return $?
[[ -d "$tree" ]] || return 0 [[ -d $tree ]] || return 0
builtin cd "$tree" || return 1 builtin cd "$tree" || return 1
} }
@ -184,7 +191,7 @@ forgit::worktree::delete() {
# register aliases # register aliases
# shellcheck disable=SC2139 # shellcheck disable=SC2139
if [[ -z "$FORGIT_NO_ALIASES" ]]; then if [[ -z $FORGIT_NO_ALIASES ]]; then
builtin export forgit_add="${forgit_add:-ga}" builtin export forgit_add="${forgit_add:-ga}"
builtin export forgit_reset_head="${forgit_reset_head:-grh}" builtin export forgit_reset_head="${forgit_reset_head:-grh}"

View file

@ -24,9 +24,9 @@ function test_forgit_clean_select_files_preview() {
assert_file_exists "$file" assert_file_exists "$file"
} }
function provider_clean_select_files() { function provider_clean_select_files() {
bashunit::data_set 1 bashunit::data_set 1
bashunit::data_set 2 bashunit::data_set 2
bashunit::data_set 3 bashunit::data_set 3
bashunit::data_set 4 bashunit::data_set 4
} }

View file

@ -13,7 +13,7 @@ function set_up_before_script() {
git config user.email "test@example.com" git config user.email "test@example.com"
git config user.name "Test User" git config user.name "Test User"
# Create an initial commit so we have a valid repo # Create an initial commit so we have a valid repo
echo "initial" > README.md echo "initial" >README.md
git add README.md git add README.md
git commit -q -m "Initial commit" git commit -q -m "Initial commit"
} }

View file

@ -13,8 +13,8 @@ function test_forgit_get_files_from_diff_line() {
local i local i
while IFS= read -r line; do while IFS= read -r line; do
actual+=( "$line" ) actual+=("$line")
done < <( echo -n "$input" | _forgit_get_files_from_diff_line | xargs -0 -n 1 echo ) done < <(echo -n "$input" | _forgit_get_files_from_diff_line | xargs -0 -n 1 echo)
# Compare array sizes # Compare array sizes
assert_same "${#expected[@]}" "${#actual[@]}" assert_same "${#expected[@]}" "${#actual[@]}"

View file

@ -2,7 +2,7 @@
FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
function set_up_before_script() { function set_up_before_script() {
source bin/git-forgit source bin/git-forgit
# Ignore global git config files # Ignore global git config files
@ -19,56 +19,55 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
git add . git add .
git commit -m "init" --quiet git commit -m "init" --quiet
echo modified > "modified file.txt" echo modified >"modified file.txt"
touch "staged file.txt" touch "staged file.txt"
git add "staged file.txt" git add "staged file.txt"
touch "untracked_file.txt" touch "untracked_file.txt"
touch 'untracked_with_\backslash' touch 'untracked_with_\backslash'
} }
function test_forgit_worktree_changes_contains_modified() { function test_forgit_worktree_changes_contains_modified() {
local output local output
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains "modified file.txt" "$output" assert_contains "modified file.txt" "$output"
} }
function test_forgit_worktree_changes_contains_untracked() { function test_forgit_worktree_changes_contains_untracked() {
local output local output
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains "untracked_file.txt" "$output" assert_contains "untracked_file.txt" "$output"
} }
function test_forgit_worktree_changes_excludes_staged() { function test_forgit_worktree_changes_excludes_staged() {
local output local output
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_not_contains "staged file.txt" "$output" assert_not_contains "staged file.txt" "$output"
} }
function test_forgit_worktree_changes_excludes_tracked() {
function test_forgit_worktree_changes_excludes_tracked() {
local output local output
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_not_contains "tracked file.txt" "$output" assert_not_contains "tracked file.txt" "$output"
} }
function test_forgit_worktree_changes_supports_backslashes() { function test_forgit_worktree_changes_supports_backslashes() {
local output local output
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains 'untracked_with_\backslash' "$output" assert_contains 'untracked_with_\backslash' "$output"
} }
function test_forgit_build_status_entries_uses_cwd_relative_display_paths_within_subdirectories() { function test_forgit_build_status_entries_uses_cwd_relative_display_paths_within_subdirectories() {
local output local output
mkdir -p dir mkdir -p dir
@ -78,9 +77,9 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains "file.txt" "$output" assert_contains "file.txt" "$output"
} }
function test_forgit_build_status_entries_prefixes_parent_paths_for_entries_outside_cwd() { function test_forgit_build_status_entries_prefixes_parent_paths_for_entries_outside_cwd() {
local output local output
mkdir -p dir other mkdir -p dir other
@ -90,9 +89,9 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains "../other/file.txt" "$output" assert_contains "../other/file.txt" "$output"
} }
function test_forgit_build_status_entries_keeps_repo_relative_paths_at_repo_root() { function test_forgit_build_status_entries_keeps_repo_relative_paths_at_repo_root() {
local output local output
mkdir -p dir mkdir -p dir
@ -101,9 +100,9 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains "dir/file.txt" "$output" assert_contains "dir/file.txt" "$output"
} }
function test_forgit_build_status_entries_uses_cwd_relative_display_paths_for_sibling_entries() { function test_forgit_build_status_entries_uses_cwd_relative_display_paths_for_sibling_entries() {
local output local output
mkdir -p dir1 dir2 mkdir -p dir1 dir2
@ -113,9 +112,9 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains "../dir1/file.txt" "$output" assert_contains "../dir1/file.txt" "$output"
} }
function test_forgit_build_status_entries_uses_cwd_relative_display_paths_from_logical_symlink_paths() { function test_forgit_build_status_entries_uses_cwd_relative_display_paths_from_logical_symlink_paths() {
local output sandbox local output sandbox
sandbox=$(bashunit::temp_dir) sandbox=$(bashunit::temp_dir)
@ -133,9 +132,9 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
output=$(_forgit_worktree_changes) output=$(_forgit_worktree_changes)
assert_contains "../dir1/file.txt" "$output" assert_contains "../dir1/file.txt" "$output"
} }
function test_forgit_worktree_changes_emits_absolute_payloads_for_subdir_entries() { function test_forgit_worktree_changes_emits_absolute_payloads_for_subdir_entries() {
local output rootdir local output rootdir
mkdir dir mkdir dir
@ -148,24 +147,24 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
assert_contains $'with_\\backslash'"$_ffsep""$rootdir/dir/with_\\backslash" "$output" assert_contains $'with_\\backslash'"$_ffsep""$rootdir/dir/with_\\backslash" "$output"
} }
function test_forgit_restore_untracked_color_colorizes_plain_untracked_lines() { function test_forgit_restore_untracked_color_colorizes_plain_untracked_lines() {
local output local output
output=$(printf '?? plain.txt\n' | _forgit_restore_untracked_color '<u>' '<r>') output=$(printf '?? plain.txt\n' | _forgit_restore_untracked_color '<u>' '<r>')
assert_same '<u>??<r> plain.txt' "$output" assert_same '<u>??<r> plain.txt' "$output"
} }
function test_forgit_restore_untracked_color_leaves_colored_lines_unchanged() { function test_forgit_restore_untracked_color_leaves_colored_lines_unchanged() {
local colored output local colored output
colored=$'\033[33m??\033[m plain.txt' colored=$'\033[33m??\033[m plain.txt'
output=$(printf '%s\n' "$colored" | _forgit_restore_untracked_color '<u>' '<r>') output=$(printf '%s\n' "$colored" | _forgit_restore_untracked_color '<u>' '<r>')
assert_same "$colored" "$output" assert_same "$colored" "$output"
} }
function test_forgit_worktree_changes_preserves_special_characters_in_payload() { function test_forgit_worktree_changes_preserves_special_characters_in_payload() {
local output path rootdir local output path rootdir
path=$'tab\t space \\ name.txt' path=$'tab\t space \\ name.txt'
@ -177,15 +176,15 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
assert_contains "${path}${_ffsep}${rootdir}/${path}" "$output" assert_contains "${path}${_ffsep}${rootdir}/${path}" "$output"
} }
function test_forgit_fzf_separator_does_not_use_literal_tabs() { function test_forgit_fzf_separator_does_not_use_literal_tabs() {
local delimiter local delimiter
delimiter=$_ffsep delimiter=$_ffsep
assert_not_contains $'\t' "$delimiter" assert_not_contains $'\t' "$delimiter"
} }
function test_forgit_worktree_changes_works_in_zsh() { function test_forgit_worktree_changes_works_in_zsh() {
local output local output
output=$( output=$(
@ -201,4 +200,4 @@ FORGIT_REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
assert_contains 'space name.txt' "$output" assert_contains 'space name.txt' "$output"
assert_contains 'back\slash.txt' "$output" assert_contains 'back\slash.txt' "$output"
assert_contains 'tab' "$output" assert_contains 'tab' "$output"
} }