mirror of
https://github.com/wfxr/forgit.git
synced 2026-09-10 07:16:23 -04:00
refactor: extract branch helpers and ANSI strip utility
- Add _forgit_strip_ansi for removing ANSI escape codes - Add _forgit_branch_list to list branches with current branch first - Add _forgit_extract_branch_name to parse branch names from git output - Update checkout_branch, switch_branch, branch_delete, cherry_pick_from_branch, and branch_preview to use the new helpers - Fix _forgit_inside_work_tree to suppress stderr
This commit is contained in:
parent
b3e8b3eae8
commit
ea58599e66
|
|
@ -31,9 +31,10 @@ $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; }
|
||||
_forgit_inside_work_tree() { git rev-parse --is-inside-work-tree >/dev/null 2>&1; }
|
||||
# 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_strip_ansi() { local ESC=$'\033'; sed "s/${ESC}\[[0-9;]*m//g"; }
|
||||
|
||||
_forgit_previous_commit() {
|
||||
# "SHA~" is invalid when the commit is the first commit, but we can use "--root" instead
|
||||
|
|
@ -151,6 +152,33 @@ _forgit_is_file_tracked() {
|
|||
git ls-files "$1" --error-unmatch &> /dev/null
|
||||
}
|
||||
|
||||
# List branches with current branch first (for use as fzf header)
|
||||
# Usage: _forgit_branch_list [git-branch-options...]
|
||||
#
|
||||
# Note: We explicitly print the current branch first rather than using
|
||||
# `LC_ALL=C sort -k1.1,1.1 -rs` because git branch output has three possible
|
||||
# prefixes: '*' (current), '+' (checked out in a worktree), and ' ' (other).
|
||||
# Sorting by the first character doesn't reliably place '*' first when '+'
|
||||
# is present, since their ASCII order (* < +) conflicts with the desired order.
|
||||
_forgit_branch_list() {
|
||||
local current
|
||||
current=$(git branch --show-current)
|
||||
if [[ -n "$current" ]]; then
|
||||
printf '\e[90m%s\e[0m\n' "* $current"
|
||||
else
|
||||
printf '\e[90m%s\e[0m\n' "* (HEAD detached at $(git rev-parse --short HEAD))"
|
||||
fi
|
||||
git branch --color=always "$@" | grep -v '^\*' | grep -v ' -> '
|
||||
}
|
||||
|
||||
# Extract branch name from git branch output
|
||||
# Handles ANSI escape codes, prefix characters (* + ' '), and symbolic refs (->)
|
||||
_forgit_extract_branch_name() {
|
||||
_forgit_strip_ansi |
|
||||
sed -E 's/^[*+ ] //; s/ -> .*//' |
|
||||
awk '{print $1}'
|
||||
}
|
||||
|
||||
_forgit_list_files() {
|
||||
local rootdir
|
||||
rootdir=$(git rev-parse --show-toplevel)
|
||||
|
|
@ -686,7 +714,9 @@ _forgit_cherry_pick() {
|
|||
}
|
||||
|
||||
_forgit_cherry_pick_from_branch_preview() {
|
||||
git log --right-only --color=always --cherry-pick --oneline "$1"..."$2"
|
||||
local branch
|
||||
branch=$(echo "$2" | _forgit_extract_branch_name)
|
||||
git log --right-only --color=always --cherry-pick --oneline "$1"..."$branch"
|
||||
}
|
||||
|
||||
_forgit_cherry_pick_from_branch() {
|
||||
|
|
@ -703,7 +733,7 @@ _forgit_cherry_pick_from_branch() {
|
|||
opts="
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
+s +m --tiebreak=index --header-lines=1
|
||||
--preview=\"$FORGIT cherry_pick_from_branch_preview '$base' {1}\"
|
||||
--preview=\"$FORGIT cherry_pick_from_branch_preview '$base' {}\"
|
||||
$FORGIT_CHERRY_PICK_FROM_BRANCH_FZF_OPTS
|
||||
"
|
||||
# loop until either the branch selector is closed or a commit to be cherry
|
||||
|
|
@ -711,10 +741,7 @@ _forgit_cherry_pick_from_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}')"
|
||||
branch="$(_forgit_branch_list --all | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)"
|
||||
else
|
||||
branch=$input_branch
|
||||
fi
|
||||
|
|
@ -888,13 +915,13 @@ _forgit_checkout_branch() {
|
|||
opts="
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
+s +m --tiebreak=index --header-lines=1
|
||||
--preview=\"$FORGIT branch_preview {1}\"
|
||||
--preview=\"$FORGIT branch_preview {}\"
|
||||
$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}')"
|
||||
branch="$(_forgit_branch_list "${_forgit_checkout_branch_branch_git_opts[@]:---all}" |
|
||||
FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)"
|
||||
[[ -z "$branch" ]] && return 1
|
||||
|
||||
# track the remote branch if possible
|
||||
|
|
@ -934,13 +961,13 @@ _forgit_switch_branch() {
|
|||
opts="
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
+s +m --tiebreak=index --header-lines=1
|
||||
--preview=\"$FORGIT branch_preview {1}\"
|
||||
--preview=\"$FORGIT branch_preview {}\"
|
||||
$FORGIT_SWITCH_BRANCH_FZF_OPTS
|
||||
"
|
||||
_forgit_switch_branch_branch_git_opts=()
|
||||
_forgit_parse_array _forgit_switch_branch_branch_git_opts "$FORGIT_SWITCH_BRANCH_BRANCH_GIT_OPTS"
|
||||
branch="$(git branch --color=always "${_forgit_switch_branch_branch_git_opts[@]:---all}" | LC_ALL=C sort -k1.1,1.1 -rs |
|
||||
FZF_DEFAULT_OPTS="$opts" fzf | awk '{print $1}')"
|
||||
branch="$(_forgit_branch_list "${_forgit_switch_branch_branch_git_opts[@]:---all}" |
|
||||
FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)"
|
||||
[[ -z "$branch" ]] && return 1
|
||||
|
||||
# track the remote branch if possible
|
||||
|
|
@ -1012,9 +1039,11 @@ _forgit_checkout_commit() {
|
|||
}
|
||||
|
||||
_forgit_branch_preview() {
|
||||
local branch
|
||||
branch=$(echo "$1" | _forgit_extract_branch_name)
|
||||
# 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[@]}" --
|
||||
git log "$branch" "${_forgit_log_preview_options[@]}" --
|
||||
}
|
||||
|
||||
_forgit_git_branch_delete() {
|
||||
|
|
@ -1031,14 +1060,10 @@ _forgit_branch_delete() {
|
|||
opts="
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
+s --multi --tiebreak=index --header-lines=1
|
||||
--preview=\"$FORGIT branch_preview {1}\"
|
||||
--preview=\"$FORGIT branch_preview {}\"
|
||||
$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}')
|
||||
for branch in $(_forgit_branch_list | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_extract_branch_name)
|
||||
do
|
||||
_forgit_git_branch_delete "$branch"
|
||||
done
|
||||
|
|
|
|||
41
tests/branch-helpers.test.sh
Normal file
41
tests/branch-helpers.test.sh
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function set_up_before_script() {
|
||||
source bin/git-forgit
|
||||
}
|
||||
|
||||
# --- _forgit_extract_branch_name ---
|
||||
|
||||
# @data_provider provider_extract_branch_name
|
||||
function test_forgit_extract_branch_name() {
|
||||
local -r input="$1"
|
||||
local -r expected="$2"
|
||||
local actual
|
||||
actual=$(echo "$input" | _forgit_extract_branch_name)
|
||||
assert_same "$expected" "$actual"
|
||||
}
|
||||
|
||||
function provider_extract_branch_name() {
|
||||
bashunit::data_set " main" "main"
|
||||
bashunit::data_set "* main" "main"
|
||||
bashunit::data_set "+ feature" "feature"
|
||||
bashunit::data_set " remotes/origin/HEAD -> origin/main" "remotes/origin/HEAD"
|
||||
bashunit::data_set " remotes/origin/feature/foo" "remotes/origin/feature/foo"
|
||||
}
|
||||
|
||||
# --- _forgit_strip_ansi ---
|
||||
|
||||
# @data_provider provider_strip_ansi
|
||||
function test_forgit_strip_ansi() {
|
||||
local -r input="$1"
|
||||
local -r expected="$2"
|
||||
local actual
|
||||
actual=$(printf '%b' "$input" | _forgit_strip_ansi)
|
||||
assert_same "$expected" "$actual"
|
||||
}
|
||||
|
||||
function provider_strip_ansi() {
|
||||
bashunit::data_set '\e[32mfoo\e[0m' "foo"
|
||||
bashunit::data_set '\e[1;31mbar\e[0m' "bar"
|
||||
bashunit::data_set 'no-ansi' "no-ansi"
|
||||
}
|
||||
Loading…
Reference in a new issue