gitbasher 5.0.1 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +32 -16
- package/dist/gitb +350 -147
- package/package.json +1 -1
package/dist/gitb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -o pipefail
|
|
3
|
-
GITBASHER_VERSION="5.0
|
|
3
|
+
GITBASHER_VERSION="5.1.0"
|
|
4
4
|
if [ "$1" == "init" ] || [ "$1" == "i" ]; then
|
|
5
5
|
git init
|
|
6
6
|
fi
|
|
@@ -839,29 +839,7 @@ function config_source_tag {
|
|
|
839
839
|
default) echo -e "${GRAY}(default)${ENDCOLOR}" ;;
|
|
840
840
|
esac
|
|
841
841
|
}
|
|
842
|
-
function
|
|
843
|
-
echo -e "${YELLOW}Current configuration:${ENDCOLOR}"
|
|
844
|
-
local user_name=$(get_config_value user.name)
|
|
845
|
-
if [ -n "$user_name" ]; then
|
|
846
|
-
echo -e "\tuser.name:\t${YELLOW}$user_name${ENDCOLOR} $(config_source_tag user.name)"
|
|
847
|
-
else
|
|
848
|
-
echo -e "\tuser.name:\t${RED}not set${ENDCOLOR}"
|
|
849
|
-
fi
|
|
850
|
-
local user_email=$(get_config_value user.email)
|
|
851
|
-
if [ -n "$user_email" ]; then
|
|
852
|
-
echo -e "\tuser.email:\t${YELLOW}$user_email${ENDCOLOR} $(config_source_tag user.email)"
|
|
853
|
-
else
|
|
854
|
-
echo -e "\tuser.email:\t${RED}not set${ENDCOLOR}"
|
|
855
|
-
fi
|
|
856
|
-
echo -e "\tdefault:\t${YELLOW}$main_branch${ENDCOLOR} $(config_source_tag gitbasher.branch)"
|
|
857
|
-
echo -e "\tseparator:\t${YELLOW}$sep${ENDCOLOR} $(config_source_tag gitbasher.sep)"
|
|
858
|
-
echo -e "\teditor:\t\t${YELLOW}$editor${ENDCOLOR} $(config_source_tag core.editor)"
|
|
859
|
-
if [ "$ticket_name" != "" ]; then
|
|
860
|
-
echo -e "\tticket:\t\t${YELLOW}$ticket_name${ENDCOLOR} $(config_source_tag gitbasher.ticket)"
|
|
861
|
-
fi
|
|
862
|
-
if [ "$scopes" != "" ]; then
|
|
863
|
-
echo -e "\tscopes:\t\t${YELLOW}$scopes${ENDCOLOR} $(config_source_tag gitbasher.scopes)"
|
|
864
|
-
fi
|
|
842
|
+
function print_ai_configuration {
|
|
865
843
|
local ai_provider=$(get_ai_provider)
|
|
866
844
|
echo -e "\tAI provider:\t${GREEN}$ai_provider${ENDCOLOR} $(config_source_tag gitbasher.ai-provider)"
|
|
867
845
|
local ai_base_url=$(get_ai_base_url)
|
|
@@ -903,6 +881,31 @@ function print_configuration {
|
|
|
903
881
|
echo -e "\t\t\t${GREEN}$(get_ai_model_for full)${ENDCOLOR} ${GRAY}(full)${ENDCOLOR} $(config_source_tag gitbasher.ai-model-full)"
|
|
904
882
|
echo -e "\t\t\t${GREEN}$(get_ai_model_for grouping)${ENDCOLOR} ${GRAY}(grouping)${ENDCOLOR} $(config_source_tag gitbasher.ai-model-grouping)"
|
|
905
883
|
fi
|
|
884
|
+
}
|
|
885
|
+
function print_configuration {
|
|
886
|
+
echo -e "${YELLOW}Current configuration:${ENDCOLOR}"
|
|
887
|
+
local user_name=$(get_config_value user.name)
|
|
888
|
+
if [ -n "$user_name" ]; then
|
|
889
|
+
echo -e "\tuser.name:\t${YELLOW}$user_name${ENDCOLOR} $(config_source_tag user.name)"
|
|
890
|
+
else
|
|
891
|
+
echo -e "\tuser.name:\t${RED}not set${ENDCOLOR}"
|
|
892
|
+
fi
|
|
893
|
+
local user_email=$(get_config_value user.email)
|
|
894
|
+
if [ -n "$user_email" ]; then
|
|
895
|
+
echo -e "\tuser.email:\t${YELLOW}$user_email${ENDCOLOR} $(config_source_tag user.email)"
|
|
896
|
+
else
|
|
897
|
+
echo -e "\tuser.email:\t${RED}not set${ENDCOLOR}"
|
|
898
|
+
fi
|
|
899
|
+
echo -e "\tdefault:\t${YELLOW}$main_branch${ENDCOLOR} $(config_source_tag gitbasher.branch)"
|
|
900
|
+
echo -e "\tseparator:\t${YELLOW}$sep${ENDCOLOR} $(config_source_tag gitbasher.sep)"
|
|
901
|
+
echo -e "\teditor:\t\t${YELLOW}$editor${ENDCOLOR} $(config_source_tag core.editor)"
|
|
902
|
+
if [ "$ticket_name" != "" ]; then
|
|
903
|
+
echo -e "\tticket:\t\t${YELLOW}$ticket_name${ENDCOLOR} $(config_source_tag gitbasher.ticket)"
|
|
904
|
+
fi
|
|
905
|
+
if [ "$scopes" != "" ]; then
|
|
906
|
+
echo -e "\tscopes:\t\t${YELLOW}$scopes${ENDCOLOR} $(config_source_tag gitbasher.scopes)"
|
|
907
|
+
fi
|
|
908
|
+
print_ai_configuration
|
|
906
909
|
local ai_proxy=$(get_ai_proxy)
|
|
907
910
|
if [ -n "$ai_proxy" ]; then
|
|
908
911
|
echo -e "\tAI proxy:\t${GREEN}$ai_proxy${ENDCOLOR} $(config_source_tag gitbasher.ai-proxy)"
|
|
@@ -1561,7 +1564,7 @@ function unset_config_value {
|
|
|
1561
1564
|
if [ -n "$global_config" ]; then
|
|
1562
1565
|
echo
|
|
1563
1566
|
echo -e "${YELLOW}Global $1 is also configured: ${BLUE}$global_config${ENDCOLOR}"
|
|
1564
|
-
echo -e "Do you want to clear it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
1567
|
+
echo -e "Do you want to clear it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
1565
1568
|
local _unset_key=""
|
|
1566
1569
|
read_key _unset_key || _unset_key="n"
|
|
1567
1570
|
if is_yes "$_unset_key"; then
|
|
@@ -1826,8 +1829,8 @@ function is_valid_model_id {
|
|
|
1826
1829
|
}
|
|
1827
1830
|
function ai_provider_requires_api_key {
|
|
1828
1831
|
case "$(get_ai_provider)" in
|
|
1829
|
-
ollama) return 1 ;;
|
|
1830
|
-
*)
|
|
1832
|
+
ollama|claude) return 1 ;;
|
|
1833
|
+
*) return 0 ;;
|
|
1831
1834
|
esac
|
|
1832
1835
|
}
|
|
1833
1836
|
function get_ai_model {
|
|
@@ -1836,18 +1839,22 @@ function get_ai_model {
|
|
|
1836
1839
|
function set_ai_model {
|
|
1837
1840
|
set_config_value gitbasher.ai-model "$1"
|
|
1838
1841
|
}
|
|
1839
|
-
readonly AI_DEFAULT_MODEL_SIMPLE="google/gemini-3.1-flash-lite
|
|
1840
|
-
readonly AI_DEFAULT_MODEL_SUBJECT="google/gemini-3.1-flash-lite
|
|
1841
|
-
readonly AI_DEFAULT_MODEL_FULL="google/gemini-3-flash
|
|
1842
|
-
readonly AI_DEFAULT_MODEL_GROUPING="
|
|
1843
|
-
readonly AI_DEFAULT_MODEL_SIMPLE_OPENAI="gpt-5.4-
|
|
1844
|
-
readonly AI_DEFAULT_MODEL_SUBJECT_OPENAI="gpt-5.4-
|
|
1842
|
+
readonly AI_DEFAULT_MODEL_SIMPLE="google/gemini-3.1-flash-lite"
|
|
1843
|
+
readonly AI_DEFAULT_MODEL_SUBJECT="google/gemini-3.1-flash-lite"
|
|
1844
|
+
readonly AI_DEFAULT_MODEL_FULL="google/gemini-3.5-flash"
|
|
1845
|
+
readonly AI_DEFAULT_MODEL_GROUPING="google/gemini-3.5-flash"
|
|
1846
|
+
readonly AI_DEFAULT_MODEL_SIMPLE_OPENAI="gpt-5.4-mini"
|
|
1847
|
+
readonly AI_DEFAULT_MODEL_SUBJECT_OPENAI="gpt-5.4-mini"
|
|
1845
1848
|
readonly AI_DEFAULT_MODEL_FULL_OPENAI="gpt-5.4-mini"
|
|
1846
1849
|
readonly AI_DEFAULT_MODEL_GROUPING_OPENAI="gpt-5.4-mini"
|
|
1847
1850
|
readonly AI_DEFAULT_MODEL_SIMPLE_OLLAMA="qwen3:8b"
|
|
1848
1851
|
readonly AI_DEFAULT_MODEL_SUBJECT_OLLAMA="qwen3:8b"
|
|
1849
1852
|
readonly AI_DEFAULT_MODEL_FULL_OLLAMA="qwen3:8b"
|
|
1850
1853
|
readonly AI_DEFAULT_MODEL_GROUPING_OLLAMA="qwen3:8b"
|
|
1854
|
+
readonly AI_DEFAULT_MODEL_SIMPLE_CLAUDE="haiku"
|
|
1855
|
+
readonly AI_DEFAULT_MODEL_SUBJECT_CLAUDE="haiku"
|
|
1856
|
+
readonly AI_DEFAULT_MODEL_FULL_CLAUDE="sonnet"
|
|
1857
|
+
readonly AI_DEFAULT_MODEL_GROUPING_CLAUDE="sonnet"
|
|
1851
1858
|
function get_ai_model_for {
|
|
1852
1859
|
local task="$1"
|
|
1853
1860
|
local task_model
|
|
@@ -1881,6 +1888,15 @@ function get_ai_model_for {
|
|
|
1881
1888
|
*) echo "$AI_DEFAULT_MODEL_SIMPLE_OLLAMA" ;;
|
|
1882
1889
|
esac
|
|
1883
1890
|
;;
|
|
1891
|
+
claude)
|
|
1892
|
+
case "$task" in
|
|
1893
|
+
simple) echo "$AI_DEFAULT_MODEL_SIMPLE_CLAUDE" ;;
|
|
1894
|
+
subject) echo "$AI_DEFAULT_MODEL_SUBJECT_CLAUDE" ;;
|
|
1895
|
+
full) echo "$AI_DEFAULT_MODEL_FULL_CLAUDE" ;;
|
|
1896
|
+
grouping) echo "$AI_DEFAULT_MODEL_GROUPING_CLAUDE" ;;
|
|
1897
|
+
*) echo "$AI_DEFAULT_MODEL_SIMPLE_CLAUDE" ;;
|
|
1898
|
+
esac
|
|
1899
|
+
;;
|
|
1884
1900
|
*)
|
|
1885
1901
|
case "$task" in
|
|
1886
1902
|
simple) echo "$AI_DEFAULT_MODEL_SIMPLE" ;;
|
|
@@ -2176,6 +2192,17 @@ function ai_smoke_check {
|
|
|
2176
2192
|
echo -e "${GREEN}✓ Ollama reachable at ${base} (${count} model(s))${ENDCOLOR}"
|
|
2177
2193
|
return 0
|
|
2178
2194
|
fi
|
|
2195
|
+
if [ "$provider" = "claude" ]; then
|
|
2196
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
2197
|
+
echo -e "${RED}✗ claude CLI not found on PATH.${ENDCOLOR}" >&2
|
|
2198
|
+
echo -e "${CYAN}💡 Install it with ${BLUE}npm install -g @anthropic-ai/claude-code${ENDCOLOR}${CYAN}, then sign in once.${ENDCOLOR}" >&2
|
|
2199
|
+
return 1
|
|
2200
|
+
fi
|
|
2201
|
+
local claude_version
|
|
2202
|
+
claude_version=$(claude --version 2>/dev/null | head -1)
|
|
2203
|
+
echo -e "${GREEN}✓ claude CLI available${claude_version:+ (}${claude_version}${claude_version:+)}${ENDCOLOR}"
|
|
2204
|
+
return 0
|
|
2205
|
+
fi
|
|
2179
2206
|
local api_key
|
|
2180
2207
|
api_key=$(get_ai_api_key)
|
|
2181
2208
|
if [ -z "$api_key" ]; then
|
|
@@ -2230,11 +2257,38 @@ function get_ai_timeout {
|
|
|
2230
2257
|
printf '%s\n' "$t"
|
|
2231
2258
|
return
|
|
2232
2259
|
fi
|
|
2233
|
-
|
|
2234
|
-
printf '300\n'
|
|
2260
|
+
case "$(get_ai_provider)" in
|
|
2261
|
+
ollama|claude) printf '300\n' ;;
|
|
2262
|
+
*) printf '60\n' ;;
|
|
2263
|
+
esac
|
|
2264
|
+
}
|
|
2265
|
+
function _call_claude_cli {
|
|
2266
|
+
local system_prompt="$1"
|
|
2267
|
+
local user_prompt="$2"
|
|
2268
|
+
local model="$3"
|
|
2269
|
+
local timeout_s output rc
|
|
2270
|
+
timeout_s=$(get_ai_timeout)
|
|
2271
|
+
if command -v perl >/dev/null 2>&1; then
|
|
2272
|
+
output=$(printf '%s\n\n%s' "$system_prompt" "$user_prompt" \
|
|
2273
|
+
| perl -e 'alarm shift; exec @ARGV' "$timeout_s" \
|
|
2274
|
+
claude -p --model "$model" --output-format text 2>/dev/null)
|
|
2275
|
+
rc=$?
|
|
2235
2276
|
else
|
|
2236
|
-
printf '
|
|
2277
|
+
output=$(printf '%s\n\n%s' "$system_prompt" "$user_prompt" \
|
|
2278
|
+
| claude -p --model "$model" --output-format text 2>/dev/null)
|
|
2279
|
+
rc=$?
|
|
2237
2280
|
fi
|
|
2281
|
+
if [ "$rc" -eq 142 ]; then
|
|
2282
|
+
echo -e "${RED}✗ claude timed out after ${timeout_s}s.${ENDCOLOR}" >&2
|
|
2283
|
+
echo -e "${YELLOW}Raise the limit with:${ENDCOLOR} ${GREEN}git config gitbasher.ai-timeout <seconds>${ENDCOLOR}" >&2
|
|
2284
|
+
return 1
|
|
2285
|
+
fi
|
|
2286
|
+
if [ "$rc" -ne 0 ] || [ -z "$output" ]; then
|
|
2287
|
+
echo -e "${RED}✗ claude CLI failed (exit ${rc}).${ENDCOLOR}" >&2
|
|
2288
|
+
echo -e "${CYAN}💡 Check it works: ${BLUE}echo hi | claude -p${ENDCOLOR}${CYAN} — you may need to sign in first.${ENDCOLOR}" >&2
|
|
2289
|
+
return 1
|
|
2290
|
+
fi
|
|
2291
|
+
printf '%s\n' "$output"
|
|
2238
2292
|
}
|
|
2239
2293
|
function call_ai_api {
|
|
2240
2294
|
local system_prompt="$1"
|
|
@@ -2253,6 +2307,17 @@ function call_ai_api {
|
|
|
2253
2307
|
clear_sensitive_vars
|
|
2254
2308
|
return 1
|
|
2255
2309
|
fi
|
|
2310
|
+
if [ "$provider" = "claude" ]; then
|
|
2311
|
+
local claude_out
|
|
2312
|
+
claude_out=$(_call_claude_cli "$system_prompt" "$user_prompt" "$model")
|
|
2313
|
+
if [ $? -ne 0 ] || [ -z "$claude_out" ]; then
|
|
2314
|
+
clear_sensitive_vars
|
|
2315
|
+
return 1
|
|
2316
|
+
fi
|
|
2317
|
+
clear_sensitive_vars
|
|
2318
|
+
echo "$claude_out"
|
|
2319
|
+
return 0
|
|
2320
|
+
fi
|
|
2256
2321
|
local tokens_field="max_tokens"
|
|
2257
2322
|
if [ "$provider" = "openai" ]; then
|
|
2258
2323
|
tokens_field="max_completion_tokens"
|
|
@@ -2496,7 +2561,16 @@ function call_ai_api {
|
|
|
2496
2561
|
echo "$ai_response"
|
|
2497
2562
|
}
|
|
2498
2563
|
function check_ai_available {
|
|
2499
|
-
|
|
2564
|
+
local active_provider
|
|
2565
|
+
active_provider=$(get_ai_provider)
|
|
2566
|
+
if [ "$active_provider" = "claude" ]; then
|
|
2567
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
2568
|
+
echo -e "${RED}✗ claude CLI not found on PATH.${ENDCOLOR}" >&2
|
|
2569
|
+
echo -e "${YELLOW}Install it with:${ENDCOLOR} ${GREEN}npm install -g @anthropic-ai/claude-code${ENDCOLOR}" >&2
|
|
2570
|
+
echo -e "${YELLOW}Or switch provider with:${ENDCOLOR} ${GREEN}gitb cfg provider${ENDCOLOR}" >&2
|
|
2571
|
+
return 1
|
|
2572
|
+
fi
|
|
2573
|
+
elif ! command -v curl >/dev/null 2>&1; then
|
|
2500
2574
|
echo -e "${RED}curl is required for AI functionality but not installed${ENDCOLOR}" >&2
|
|
2501
2575
|
return 1
|
|
2502
2576
|
fi
|
|
@@ -2732,7 +2806,7 @@ function set_default_branch {
|
|
|
2732
2806
|
echo -e "${GREEN}✓ Set '${branch_name}' as the default gitbasher branch in '${project_name}'${ENDCOLOR}"
|
|
2733
2807
|
echo
|
|
2734
2808
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
2735
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
2809
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
2736
2810
|
yes_no_choice "\nSet '${branch_name}' globally" "true"
|
|
2737
2811
|
main_branch=$(set_config_value gitbasher.branch $branch_name "true")
|
|
2738
2812
|
}
|
|
@@ -2770,7 +2844,7 @@ function set_sep {
|
|
|
2770
2844
|
echo -e "${GREEN}✓ Set '${sep}' as the branch name separator in '${project_name}'${ENDCOLOR}"
|
|
2771
2845
|
echo
|
|
2772
2846
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
2773
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
2847
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
2774
2848
|
yes_no_choice "\nSet '${sep}' globally" "true"
|
|
2775
2849
|
sep=$(set_config_value gitbasher.sep "$new_sep" "true")
|
|
2776
2850
|
}
|
|
@@ -2800,7 +2874,7 @@ function set_editor {
|
|
|
2800
2874
|
echo -e "${GREEN}✓ Using editor '$editor' (${which_output})${ENDCOLOR}"
|
|
2801
2875
|
echo
|
|
2802
2876
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
2803
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
2877
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
2804
2878
|
yes_no_choice "\nSet '${editor}' globally" "true"
|
|
2805
2879
|
editor=$(set_config_value core.editor "$choice" "true")
|
|
2806
2880
|
}
|
|
@@ -2833,12 +2907,15 @@ function set_ticket {
|
|
|
2833
2907
|
echo -e "${GREEN}✓ Set '${ticket_name}' as the ticket prefix in '${project_name}'${ENDCOLOR}"
|
|
2834
2908
|
echo
|
|
2835
2909
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
2836
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
2910
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
2837
2911
|
yes_no_choice "\nSet '${ticket_name}' globally" "true"
|
|
2838
2912
|
ticket_name=$(set_config_value gitbasher.ticket "$ticket_name" "true")
|
|
2839
2913
|
}
|
|
2840
2914
|
function configure_ai_key {
|
|
2841
|
-
|
|
2915
|
+
local _chained=""
|
|
2916
|
+
[ "$1" = "--chained" ] && _chained="true"
|
|
2917
|
+
if [ -z "$_chained" ] \
|
|
2918
|
+
&& [ -z "$(git config --get gitbasher.ai-provider 2>/dev/null)" ] \
|
|
2842
2919
|
&& [ -z "$(git config --global --get gitbasher.ai-provider 2>/dev/null)" ]; then
|
|
2843
2920
|
configure_ai_provider
|
|
2844
2921
|
return
|
|
@@ -2848,9 +2925,10 @@ function configure_ai_key {
|
|
|
2848
2925
|
provider_upper=$(echo "$provider" | tr '[:lower:]' '[:upper:]')
|
|
2849
2926
|
echo -e "${YELLOW}Enter AI API key for provider '${provider}'${ENDCOLOR}"
|
|
2850
2927
|
echo
|
|
2851
|
-
if
|
|
2928
|
+
if ! ai_provider_requires_api_key; then
|
|
2852
2929
|
echo -e "${YELLOW}Provider '${provider}' does not require an API key.${ENDCOLOR}"
|
|
2853
2930
|
echo -e "Switch providers with ${GREEN}gitb cfg provider${ENDCOLOR} to set one."
|
|
2931
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2854
2932
|
exit
|
|
2855
2933
|
fi
|
|
2856
2934
|
ai_api_key=$(get_ai_api_key)
|
|
@@ -2858,6 +2936,24 @@ function configure_ai_key {
|
|
|
2858
2936
|
echo -e "${YELLOW}No AI API key is set for '${provider}'.${ENDCOLOR}"
|
|
2859
2937
|
else
|
|
2860
2938
|
echo -e "AI API key for '${provider}' is ${GREEN}configured${ENDCOLOR}: ${BLUE}$(mask_api_key "$ai_api_key")${ENDCOLOR}"
|
|
2939
|
+
echo
|
|
2940
|
+
echo -e "Press ${YELLOW}Enter${ENDCOLOR} to keep it, ${YELLOW}r${ENDCOLOR} to replace, or ${YELLOW}0${ENDCOLOR} to remove"
|
|
2941
|
+
local _key_action=""
|
|
2942
|
+
read_key _key_action || { if [ -n "$_chained" ]; then return 130; fi; exit; }
|
|
2943
|
+
if [ "$_key_action" = "0" ]; then
|
|
2944
|
+
unset_ai_api_key
|
|
2945
|
+
echo
|
|
2946
|
+
echo -e "${GREEN}✓ Removed AI API key for '${provider}' from '${project_name}'${ENDCOLOR}"
|
|
2947
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2948
|
+
exit
|
|
2949
|
+
fi
|
|
2950
|
+
normalize_key "$_key_action"
|
|
2951
|
+
if [ "$normalized_key" != "r" ]; then
|
|
2952
|
+
echo -e "Keeping the existing key."
|
|
2953
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2954
|
+
exit
|
|
2955
|
+
fi
|
|
2956
|
+
echo
|
|
2861
2957
|
fi
|
|
2862
2958
|
case "$provider" in
|
|
2863
2959
|
openai)
|
|
@@ -2882,7 +2978,8 @@ function configure_ai_key {
|
|
|
2882
2978
|
echo -e " ${GREEN}env var${ENDCOLOR} — recommended; the key never touches disk via gitbasher"
|
|
2883
2979
|
echo -e " ${BLUE}git config${ENDCOLOR} — convenient, but stored in plaintext under .git/config (or ~/.gitconfig if global)"
|
|
2884
2980
|
echo
|
|
2885
|
-
read -n 1 -p "Use environment variable (recommended)? (
|
|
2981
|
+
read -n 1 -p "Use environment variable (recommended)? (Y/n) " ai_storage_choice \
|
|
2982
|
+
|| { if [ -n "$_chained" ]; then echo; return 130; fi; ai_storage_choice="n"; }
|
|
2886
2983
|
echo
|
|
2887
2984
|
if is_yes "$ai_storage_choice"; then
|
|
2888
2985
|
echo
|
|
@@ -2892,6 +2989,7 @@ function configure_ai_key {
|
|
|
2892
2989
|
echo -e "${CYAN}gitbasher checks env vars before git config every run, so this takes precedence.${ENDCOLOR}"
|
|
2893
2990
|
echo -e "Append it now with:"
|
|
2894
2991
|
echo -e " ${GREEN}echo \"export GITB_AI_API_KEY_${provider_upper}='your-${provider}-key'\" >> $(printf '%q' "$rc_file")${ENDCOLOR}"
|
|
2992
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2895
2993
|
exit
|
|
2896
2994
|
fi
|
|
2897
2995
|
echo
|
|
@@ -2901,20 +2999,24 @@ function configure_ai_key {
|
|
|
2901
2999
|
echo -e "${YELLOW}Input is hidden — type the key and press Enter.${ENDCOLOR}"
|
|
2902
3000
|
read_silent_input ai_key_input "API Key: "
|
|
2903
3001
|
if [ "$ai_key_input" == "" ]; then
|
|
3002
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2904
3003
|
exit
|
|
2905
3004
|
fi
|
|
2906
3005
|
if [ "$ai_key_input" == "0" ]; then
|
|
2907
3006
|
unset_ai_api_key
|
|
2908
3007
|
echo
|
|
2909
3008
|
echo -e "${GREEN}✓ Removed AI API key for '${provider}' from '${project_name}'${ENDCOLOR}"
|
|
3009
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2910
3010
|
exit
|
|
2911
3011
|
fi
|
|
2912
3012
|
echo
|
|
2913
3013
|
if [[ ! "$ai_key_input" =~ ^[a-zA-Z0-9._-]{20,}$ ]]; then
|
|
2914
3014
|
echo -e "${YELLOW}⚠ API key format does not look like a valid ${provider} key.${ENDCOLOR}" >&2
|
|
2915
|
-
read -n 1 -p "Continue anyway? (
|
|
3015
|
+
read -n 1 -p "Continue anyway? (Y/n) " -s choice \
|
|
3016
|
+
|| { if [ -n "$_chained" ]; then echo; return 130; fi; choice="n"; }
|
|
2916
3017
|
echo
|
|
2917
3018
|
if ! is_yes "$choice"; then
|
|
3019
|
+
if [ -n "$_chained" ]; then return 1; fi
|
|
2918
3020
|
exit 1
|
|
2919
3021
|
fi
|
|
2920
3022
|
fi
|
|
@@ -2923,14 +3025,24 @@ function configure_ai_key {
|
|
|
2923
3025
|
echo
|
|
2924
3026
|
ai_smoke_check || true
|
|
2925
3027
|
echo
|
|
2926
|
-
[ "$GITBASHER_NO_REPO" = "true" ]
|
|
2927
|
-
|
|
3028
|
+
if [ "$GITBASHER_NO_REPO" = "true" ]; then
|
|
3029
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
3030
|
+
exit
|
|
3031
|
+
fi
|
|
3032
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
2928
3033
|
echo -e "${RED}⚠ Global API keys are stored in plaintext in ~/.gitconfig.${ENDCOLOR}"
|
|
2929
3034
|
echo -e "${CYAN}💡 For better security, set ${BLUE}GITB_AI_API_KEY_${provider_upper}${CYAN} as an environment variable instead.${ENDCOLOR}"
|
|
2930
|
-
|
|
2931
|
-
|
|
3035
|
+
local _key_global=""
|
|
3036
|
+
read_key _key_global || { if [ -n "$_chained" ]; then return 130; fi; _key_global="n"; }
|
|
3037
|
+
if is_yes "$_key_global"; then
|
|
3038
|
+
echo -e "${YELLOW}Set AI API key globally${ENDCOLOR}"
|
|
3039
|
+
ai_api_key=$(set_ai_api_key "$ai_key_input" "true")
|
|
3040
|
+
fi
|
|
3041
|
+
return 0
|
|
2932
3042
|
}
|
|
2933
3043
|
function configure_ai_provider {
|
|
3044
|
+
local _chained=""
|
|
3045
|
+
[ "$1" = "--chained" ] && _chained="true"
|
|
2934
3046
|
echo -e "${YELLOW}Choose AI Provider${ENDCOLOR}"
|
|
2935
3047
|
echo
|
|
2936
3048
|
local current_provider=$(get_ai_provider)
|
|
@@ -2944,10 +3056,15 @@ function configure_ai_provider {
|
|
|
2944
3056
|
echo -e " 1. ${BLUE}openrouter${ENDCOLOR} Aggregator with hundreds of models — needs an API key"
|
|
2945
3057
|
echo -e " 2. ${BLUE}openai${ENDCOLOR} OpenAI direct (GPT-5 family) — needs an API key"
|
|
2946
3058
|
echo -e " 3. ${BLUE}ollama${ENDCOLOR} Local or remote models (you'll set the host) — no API key required"
|
|
3059
|
+
echo -e " 4. ${BLUE}claude${ENDCOLOR} Local Claude Code CLI (claude -p) — uses your Claude account, no API key"
|
|
2947
3060
|
echo
|
|
2948
3061
|
echo -e "Press Enter to keep the current provider, or enter 0 to reset to default (${AI_DEFAULT_PROVIDER})"
|
|
2949
|
-
read_editable_input choice "Choice (1-
|
|
3062
|
+
if ! read_editable_input choice "Choice (1-4): "; then
|
|
3063
|
+
if [ -n "$_chained" ]; then return 130; fi
|
|
3064
|
+
exit
|
|
3065
|
+
fi
|
|
2950
3066
|
if [ "$choice" == "" ]; then
|
|
3067
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2951
3068
|
exit
|
|
2952
3069
|
fi
|
|
2953
3070
|
if [ "$choice" == "0" ]; then
|
|
@@ -2955,6 +3072,7 @@ function configure_ai_provider {
|
|
|
2955
3072
|
git config --global --unset gitbasher.ai-provider 2>/dev/null
|
|
2956
3073
|
echo
|
|
2957
3074
|
echo -e "${GREEN}✓ Reset provider to default (${AI_DEFAULT_PROVIDER})${ENDCOLOR}"
|
|
3075
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
2958
3076
|
exit
|
|
2959
3077
|
fi
|
|
2960
3078
|
local new_provider
|
|
@@ -2963,8 +3081,10 @@ function configure_ai_provider {
|
|
|
2963
3081
|
1) new_provider="openrouter" ;;
|
|
2964
3082
|
2) new_provider="openai" ;;
|
|
2965
3083
|
3) new_provider="ollama" ;;
|
|
3084
|
+
4) new_provider="claude" ;;
|
|
2966
3085
|
*)
|
|
2967
3086
|
echo -e "${RED}✗ Invalid choice.${ENDCOLOR}" >&2
|
|
3087
|
+
if [ -n "$_chained" ]; then return 1; fi
|
|
2968
3088
|
exit 1
|
|
2969
3089
|
;;
|
|
2970
3090
|
esac
|
|
@@ -2987,6 +3107,7 @@ function configure_ai_provider {
|
|
|
2987
3107
|
host_input="${host_input%/}"
|
|
2988
3108
|
if ! validate_proxy_url "$host_input"; then
|
|
2989
3109
|
echo -e "${RED}✗ '$host_input' does not look like a valid host URL (expected http://host[:port]).${ENDCOLOR}"
|
|
3110
|
+
if [ -n "$_chained" ]; then return 1; fi
|
|
2990
3111
|
exit 1
|
|
2991
3112
|
fi
|
|
2992
3113
|
set_ai_ollama_host "$host_input" >/dev/null
|
|
@@ -2998,6 +3119,16 @@ function configure_ai_provider {
|
|
|
2998
3119
|
echo
|
|
2999
3120
|
ai_smoke_check || true
|
|
3000
3121
|
;;
|
|
3122
|
+
claude)
|
|
3123
|
+
echo
|
|
3124
|
+
if command -v claude >/dev/null 2>&1; then
|
|
3125
|
+
ai_smoke_check || true
|
|
3126
|
+
else
|
|
3127
|
+
echo -e "${RED}✗ claude CLI not found on PATH.${ENDCOLOR}"
|
|
3128
|
+
echo -e "${CYAN}💡 Install it with ${BLUE}npm install -g @anthropic-ai/claude-code${CYAN}, then sign in once.${ENDCOLOR}"
|
|
3129
|
+
fi
|
|
3130
|
+
echo -e "${CYAN}💡 Pick a model with ${GREEN}gitb cfg model${CYAN} (haiku, sonnet, opus).${ENDCOLOR}"
|
|
3131
|
+
;;
|
|
3001
3132
|
*)
|
|
3002
3133
|
if [ -n "$(get_ai_api_key)" ]; then
|
|
3003
3134
|
echo
|
|
@@ -3007,9 +3138,9 @@ function configure_ai_provider {
|
|
|
3007
3138
|
esac
|
|
3008
3139
|
echo
|
|
3009
3140
|
if [ "$GITBASHER_NO_REPO" != "true" ]; then
|
|
3010
|
-
echo -e "Do you want to set this provider ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
3141
|
+
echo -e "Do you want to set this provider ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
3011
3142
|
local _prov_key=""
|
|
3012
|
-
read_key _prov_key || _prov_key="n"
|
|
3143
|
+
read_key _prov_key || { if [ -n "$_chained" ]; then return 130; fi; _prov_key="n"; }
|
|
3013
3144
|
if is_yes "$_prov_key"; then
|
|
3014
3145
|
echo -e "${YELLOW}Set AI provider globally${ENDCOLOR}"
|
|
3015
3146
|
set_config_value gitbasher.ai-provider "$new_provider" "true"
|
|
@@ -3018,12 +3149,12 @@ function configure_ai_provider {
|
|
|
3018
3149
|
fi
|
|
3019
3150
|
fi
|
|
3020
3151
|
fi
|
|
3021
|
-
if ai_provider_requires_api_key && [ -z "$(get_ai_api_key)" ]; then
|
|
3152
|
+
if [ -z "$_chained" ] && ai_provider_requires_api_key && [ -z "$(get_ai_api_key)" ]; then
|
|
3022
3153
|
echo
|
|
3023
3154
|
echo -e "${YELLOW}No API key is set for '${new_provider}'.${ENDCOLOR}"
|
|
3024
3155
|
echo -e "${CYAN}AI commands will be blocked until a key is configured.${ENDCOLOR}"
|
|
3025
3156
|
echo
|
|
3026
|
-
echo -e "Set one now? (
|
|
3157
|
+
echo -e "Set one now? (Y/n)"
|
|
3027
3158
|
if read -n 1 -s key_choice && is_yes "$key_choice"; then
|
|
3028
3159
|
echo
|
|
3029
3160
|
configure_ai_key
|
|
@@ -3034,6 +3165,8 @@ function configure_ai_provider {
|
|
|
3034
3165
|
fi
|
|
3035
3166
|
}
|
|
3036
3167
|
function configure_ai_model {
|
|
3168
|
+
local _chained=""
|
|
3169
|
+
[ "$1" = "--chained" ] && _chained="true"
|
|
3037
3170
|
echo -e "${YELLOW}Configure AI Model${ENDCOLOR}"
|
|
3038
3171
|
echo
|
|
3039
3172
|
current_model=$(get_ai_model)
|
|
@@ -3054,9 +3187,9 @@ function configure_ai_model {
|
|
|
3054
3187
|
echo -e "Popular models for provider '${GREEN}$(get_ai_provider)${ENDCOLOR}':"
|
|
3055
3188
|
case "$(get_ai_provider)" in
|
|
3056
3189
|
openai)
|
|
3057
|
-
echo -e " • ${BLUE}gpt-5.4-
|
|
3058
|
-
echo -e " • ${BLUE}gpt-5.4-
|
|
3059
|
-
echo -e " • ${BLUE}gpt-5.
|
|
3190
|
+
echo -e " • ${BLUE}gpt-5.4-mini${ENDCOLOR} - Default: as fast as nano with better quality (~\$0.75/\$4.50 per M)"
|
|
3191
|
+
echo -e " • ${BLUE}gpt-5.4-nano${ENDCOLOR} - Budget option for high volume (~\$0.20/\$1.25 per M)"
|
|
3192
|
+
echo -e " • ${BLUE}gpt-5.5${ENDCOLOR} - Flagship, only worth it for the hardest grouping cases"
|
|
3060
3193
|
;;
|
|
3061
3194
|
ollama)
|
|
3062
3195
|
echo -e " • ${BLUE}qwen3:8b${ENDCOLOR} - Best small instruction-follower; most stable structured output"
|
|
@@ -3064,12 +3197,18 @@ function configure_ai_model {
|
|
|
3064
3197
|
echo -e " • ${BLUE}qwen2.5-coder:7b${ENDCOLOR} - Code-focused; good when most diffs are code"
|
|
3065
3198
|
echo -e " Run ${BLUE}ollama list${ENDCOLOR} to see what you have pulled locally."
|
|
3066
3199
|
;;
|
|
3200
|
+
claude)
|
|
3201
|
+
echo -e " • ${BLUE}haiku${ENDCOLOR} - Fastest and cheapest; ideal for one-line messages"
|
|
3202
|
+
echo -e " • ${BLUE}sonnet${ENDCOLOR} - Best balance for full-body prose and grouping"
|
|
3203
|
+
echo -e " • ${BLUE}opus${ENDCOLOR} - Strongest, for the hardest cases"
|
|
3204
|
+
echo -e " Aliases resolve to the current model generation; full ids (${BLUE}claude-haiku-4-5${ENDCOLOR}) also work."
|
|
3205
|
+
;;
|
|
3067
3206
|
*)
|
|
3068
3207
|
echo -e " • ${BLUE}openrouter/auto${ENDCOLOR} - Auto-select best available model"
|
|
3069
|
-
echo -e " • ${BLUE}google/gemini-3.1-flash-lite
|
|
3070
|
-
echo -e " • ${BLUE}google/gemini-3-flash
|
|
3208
|
+
echo -e " • ${BLUE}google/gemini-3.1-flash-lite${ENDCOLOR} - Fastest, cheapest (~\$0.25/\$1.50 per M)"
|
|
3209
|
+
echo -e " • ${BLUE}google/gemini-3.5-flash${ENDCOLOR} - Current flash generation, better body prose"
|
|
3071
3210
|
echo -e " • ${BLUE}anthropic/claude-haiku-4.5${ENDCOLOR} - Strict instruction following"
|
|
3072
|
-
echo -e " • ${BLUE}
|
|
3211
|
+
echo -e " • ${BLUE}anthropic/claude-sonnet-5${ENDCOLOR} - Premium quality for the hardest cases"
|
|
3073
3212
|
;;
|
|
3074
3213
|
esac
|
|
3075
3214
|
echo
|
|
@@ -3098,8 +3237,12 @@ function configure_ai_model {
|
|
|
3098
3237
|
echo
|
|
3099
3238
|
fi
|
|
3100
3239
|
echo -e "Press Enter to exit without changes, or 0 to clear the override (use per-task defaults)"
|
|
3101
|
-
read_editable_input model_input "Model ID: "
|
|
3240
|
+
if ! read_editable_input model_input "Model ID: "; then
|
|
3241
|
+
if [ -n "$_chained" ]; then return 130; fi
|
|
3242
|
+
exit
|
|
3243
|
+
fi
|
|
3102
3244
|
if [ "$model_input" == "" ]; then
|
|
3245
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
3103
3246
|
exit
|
|
3104
3247
|
fi
|
|
3105
3248
|
if [ "$model_input" == "0" ]; then
|
|
@@ -3107,6 +3250,7 @@ function configure_ai_model {
|
|
|
3107
3250
|
git config --global --unset gitbasher.ai-model 2>/dev/null
|
|
3108
3251
|
echo
|
|
3109
3252
|
echo -e "${GREEN}✓ Cleared global model override — each task now uses its per-task default${ENDCOLOR}"
|
|
3253
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
3110
3254
|
exit
|
|
3111
3255
|
fi
|
|
3112
3256
|
echo
|
|
@@ -3115,23 +3259,61 @@ function configure_ai_model {
|
|
|
3115
3259
|
model_input="${ollama_model_list[$((model_input - 1))]}"
|
|
3116
3260
|
else
|
|
3117
3261
|
echo -e "${RED}✗ Invalid selection: ${model_input}${ENDCOLOR}" >&2
|
|
3262
|
+
if [ -n "$_chained" ]; then return 1; fi
|
|
3118
3263
|
exit 1
|
|
3119
3264
|
fi
|
|
3120
3265
|
fi
|
|
3121
3266
|
if ! is_valid_model_id "$model_input"; then
|
|
3122
3267
|
echo -e "${RED}✗ Invalid model ID format.${ENDCOLOR}" >&2
|
|
3123
3268
|
echo -e "${YELLOW}Use only letters, numbers, dots, dashes, underscores, colons, and slashes.${ENDCOLOR}" >&2
|
|
3269
|
+
if [ -n "$_chained" ]; then return 1; fi
|
|
3124
3270
|
exit 1
|
|
3125
3271
|
fi
|
|
3126
3272
|
set_ai_model "$model_input"
|
|
3127
3273
|
echo -e "${GREEN}✓ Set AI model to '$model_input' for '${project_name}'${ENDCOLOR}"
|
|
3128
3274
|
echo
|
|
3129
|
-
[ "$GITBASHER_NO_REPO" = "true" ]
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3275
|
+
if [ "$GITBASHER_NO_REPO" = "true" ]; then
|
|
3276
|
+
if [ -n "$_chained" ]; then return 0; fi
|
|
3277
|
+
exit
|
|
3278
|
+
fi
|
|
3279
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
3280
|
+
local _model_global=""
|
|
3281
|
+
read_key _model_global || { if [ -n "$_chained" ]; then return 130; fi; _model_global="n"; }
|
|
3282
|
+
if is_yes "$_model_global"; then
|
|
3283
|
+
echo -e "${YELLOW}Set AI model globally${ENDCOLOR}"
|
|
3284
|
+
set_config_value gitbasher.ai-model "$model_input" "true"
|
|
3285
|
+
fi
|
|
3286
|
+
return 0
|
|
3133
3287
|
}
|
|
3134
|
-
function
|
|
3288
|
+
function configure_ai_wizard {
|
|
3289
|
+
local _rc
|
|
3290
|
+
echo -e "${YELLOW}Step 1/3 — Provider${ENDCOLOR}"
|
|
3291
|
+
echo
|
|
3292
|
+
configure_ai_provider --chained
|
|
3293
|
+
_rc=$?
|
|
3294
|
+
if [ "$_rc" -eq 130 ]; then prompt_aborted; fi
|
|
3295
|
+
echo
|
|
3296
|
+
echo -e "${YELLOW}Step 2/3 — API key${ENDCOLOR}"
|
|
3297
|
+
echo
|
|
3298
|
+
if ai_provider_requires_api_key; then
|
|
3299
|
+
configure_ai_key --chained
|
|
3300
|
+
_rc=$?
|
|
3301
|
+
if [ "$_rc" -eq 130 ]; then prompt_aborted; fi
|
|
3302
|
+
else
|
|
3303
|
+
echo -e "Provider '$(get_ai_provider)' does not require an API key — skipping."
|
|
3304
|
+
fi
|
|
3305
|
+
echo
|
|
3306
|
+
echo -e "${YELLOW}Step 3/3 — Model${ENDCOLOR}"
|
|
3307
|
+
echo
|
|
3308
|
+
configure_ai_model --chained
|
|
3309
|
+
_rc=$?
|
|
3310
|
+
if [ "$_rc" -eq 130 ]; then prompt_aborted; fi
|
|
3311
|
+
echo
|
|
3312
|
+
echo -e "${GREEN}✓ AI setup complete${ENDCOLOR}"
|
|
3313
|
+
echo
|
|
3314
|
+
print_ai_configuration
|
|
3315
|
+
}
|
|
3316
|
+
function configure_ai_proxy {
|
|
3135
3317
|
echo -e "${YELLOW}Configure AI HTTP/SOCKS Proxy${ENDCOLOR}"
|
|
3136
3318
|
echo
|
|
3137
3319
|
ai_proxy=$(get_ai_proxy)
|
|
@@ -3175,7 +3357,7 @@ function configure_ai_proxy {
|
|
|
3175
3357
|
echo -e " ${BLUE}gitb commit aif${ENDCOLOR} - Fast AI commit through proxy"
|
|
3176
3358
|
echo
|
|
3177
3359
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
3178
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
3360
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
3179
3361
|
yes_no_choice "\nSet AI proxy globally" "true"
|
|
3180
3362
|
set_ai_proxy "$ai_proxy_input"
|
|
3181
3363
|
git config --global gitbasher.ai-proxy "$ai_proxy_input"
|
|
@@ -3209,7 +3391,7 @@ function configure_ai_history {
|
|
|
3209
3391
|
if [ "$limit_input" -gt 20 ]; then
|
|
3210
3392
|
echo -e "${YELLOW}High values may exceed token limits and slow down AI responses.${ENDCOLOR}"
|
|
3211
3393
|
fi
|
|
3212
|
-
read -n 1 -p "Continue anyway? (
|
|
3394
|
+
read -n 1 -p "Continue anyway? (Y/n) " -s choice || choice="n"
|
|
3213
3395
|
echo
|
|
3214
3396
|
if ! is_yes "$choice"; then
|
|
3215
3397
|
exit
|
|
@@ -3219,7 +3401,7 @@ function configure_ai_history {
|
|
|
3219
3401
|
echo -e "${GREEN}✓ Set AI commit history limit to ${limit_input} for '${project_name}'${ENDCOLOR}"
|
|
3220
3402
|
echo
|
|
3221
3403
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
3222
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
3404
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
3223
3405
|
yes_no_choice "\nSet AI commit history limit globally" "true"
|
|
3224
3406
|
git config --global gitbasher.ai-commit-history-limit "$limit_input"
|
|
3225
3407
|
}
|
|
@@ -3257,7 +3439,7 @@ function configure_push_warn_size {
|
|
|
3257
3439
|
fi
|
|
3258
3440
|
echo
|
|
3259
3441
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
3260
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
3442
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
3261
3443
|
yes_no_choice "\nSet push size warning globally" "true"
|
|
3262
3444
|
git config --global gitbasher.push-warn-size "$limit_input"
|
|
3263
3445
|
}
|
|
@@ -3286,7 +3468,7 @@ function configure_ai_diff {
|
|
|
3286
3468
|
lines_input="$validated_number"
|
|
3287
3469
|
if [ "$lines_input" -lt 50 ] || [ "$lines_input" -gt 2000 ]; then
|
|
3288
3470
|
echo -e "${YELLOW}⚠ Value is outside the recommended range (50-2000).${ENDCOLOR}"
|
|
3289
|
-
read -n 1 -p "Continue anyway? (
|
|
3471
|
+
read -n 1 -p "Continue anyway? (Y/n) " -s choice || choice="n"
|
|
3290
3472
|
echo
|
|
3291
3473
|
if ! is_yes "$choice"; then
|
|
3292
3474
|
exit
|
|
@@ -3304,7 +3486,7 @@ function configure_ai_diff {
|
|
|
3304
3486
|
chars_input="$validated_number"
|
|
3305
3487
|
if [ "$chars_input" -lt 4000 ] || [ "$chars_input" -gt 100000 ]; then
|
|
3306
3488
|
echo -e "${YELLOW}⚠ Value is outside the recommended range (4000-100000).${ENDCOLOR}"
|
|
3307
|
-
read -n 1 -p "Continue anyway? (
|
|
3489
|
+
read -n 1 -p "Continue anyway? (Y/n) " -s choice || choice="n"
|
|
3308
3490
|
echo
|
|
3309
3491
|
if ! is_yes "$choice"; then
|
|
3310
3492
|
exit
|
|
@@ -3319,7 +3501,7 @@ function configure_ai_diff {
|
|
|
3319
3501
|
fi
|
|
3320
3502
|
echo
|
|
3321
3503
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
3322
|
-
echo -e "Do you want to apply ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
3504
|
+
echo -e "Do you want to apply ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
3323
3505
|
yes_no_choice "\nApply AI diff settings globally" "true"
|
|
3324
3506
|
if [ -n "$lines_input" ]; then
|
|
3325
3507
|
git config --global gitbasher.ai-diff-limit "$lines_input"
|
|
@@ -3367,7 +3549,7 @@ function set_scopes {
|
|
|
3367
3549
|
echo -e "${GREEN}✓ Set '${scopes}' as the scopes list in '${project_name}'${ENDCOLOR}"
|
|
3368
3550
|
echo
|
|
3369
3551
|
[ "$GITBASHER_NO_REPO" = "true" ] && exit
|
|
3370
|
-
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (
|
|
3552
|
+
echo -e "Do you want to set it ${YELLOW}globally${ENDCOLOR} for all projects (Y/n)?"
|
|
3371
3553
|
yes_no_choice "\nSet '${scopes}' globally" "true"
|
|
3372
3554
|
git config --global --replace-all gitbasher.scopes "$scopes_raw"
|
|
3373
3555
|
}
|
|
@@ -3511,7 +3693,8 @@ function config_script {
|
|
|
3511
3693
|
editor|ed|e) set_editor_cfg="true";;
|
|
3512
3694
|
ticket|jira|ti|t) set_ticket_cfg="true";;
|
|
3513
3695
|
scopes|scope|sc|s) set_scopes_cfg="true";;
|
|
3514
|
-
ai|llm
|
|
3696
|
+
ai|llm) set_ai_cfg="true";;
|
|
3697
|
+
key) set_ai_key_cfg="true";;
|
|
3515
3698
|
provider|prov) set_ai_provider_cfg="true";;
|
|
3516
3699
|
model|m) set_ai_model_cfg="true";;
|
|
3517
3700
|
proxy|prx|p) set_proxy_cfg="true";;
|
|
@@ -3536,6 +3719,8 @@ function config_script {
|
|
|
3536
3719
|
elif [ -n "${set_scopes_cfg}" ]; then
|
|
3537
3720
|
header="$header SCOPES LIST"
|
|
3538
3721
|
elif [ -n "${set_ai_cfg}" ]; then
|
|
3722
|
+
header="$header AI SETUP"
|
|
3723
|
+
elif [ -n "${set_ai_key_cfg}" ]; then
|
|
3539
3724
|
header="$header AI API KEY"
|
|
3540
3725
|
elif [ -n "${set_ai_provider_cfg}" ]; then
|
|
3541
3726
|
header="$header AI PROVIDER"
|
|
@@ -3591,6 +3776,10 @@ function config_script {
|
|
|
3591
3776
|
exit
|
|
3592
3777
|
fi
|
|
3593
3778
|
if [ "$set_ai_cfg" == "true" ]; then
|
|
3779
|
+
configure_ai_wizard
|
|
3780
|
+
exit
|
|
3781
|
+
fi
|
|
3782
|
+
if [ "$set_ai_key_cfg" == "true" ]; then
|
|
3594
3783
|
configure_ai_key
|
|
3595
3784
|
exit
|
|
3596
3785
|
fi
|
|
@@ -3638,8 +3827,9 @@ function config_script {
|
|
|
3638
3827
|
print_help_row $PAD "editor" "ed, e" "Set the editor for commit messages"
|
|
3639
3828
|
print_help_row $PAD "ticket" "ti, t, jira" "Set the ticket prefix used in commits and branches"
|
|
3640
3829
|
print_help_row $PAD "scopes" "sc" "Set the list of suggested commit scopes"
|
|
3641
|
-
print_help_row $PAD "ai" "llm
|
|
3642
|
-
print_help_row $PAD "
|
|
3830
|
+
print_help_row $PAD "ai" "llm" "Set up AI: provider, API key, and model (wizard)"
|
|
3831
|
+
print_help_row $PAD "key" "" "Set the AI API key only"
|
|
3832
|
+
print_help_row $PAD "provider" "prov" "Choose the AI provider (openrouter, openai, ollama, claude)"
|
|
3643
3833
|
print_help_row $PAD "model" "m" "Set the AI model override"
|
|
3644
3834
|
print_help_row $PAD "proxy" "prx, p" "Set an HTTP/SOCKS proxy for AI requests"
|
|
3645
3835
|
print_help_row $PAD "history" "hist" "Set how many recent commits to include in AI prompts"
|
|
@@ -3653,7 +3843,7 @@ function config_script {
|
|
|
3653
3843
|
echo -e " ${GREEN}gitb cfg${ENDCOLOR} Show current configuration"
|
|
3654
3844
|
echo -e " ${GREEN}gitb cfg user${ENDCOLOR} Set user.name and user.email"
|
|
3655
3845
|
echo -e " ${GREEN}gitb cfg default${ENDCOLOR} Choose the default gitbasher branch"
|
|
3656
|
-
echo -e " ${GREEN}gitb cfg ai${ENDCOLOR}
|
|
3846
|
+
echo -e " ${GREEN}gitb cfg ai${ENDCOLOR} Interactive AI setup (provider, key, model)"
|
|
3657
3847
|
exit
|
|
3658
3848
|
fi
|
|
3659
3849
|
print_configuration
|
|
@@ -3718,7 +3908,7 @@ function merge_script {
|
|
|
3718
3908
|
elif [ "$current_local_commit" != "$current_remote_commit" ]; then
|
|
3719
3909
|
echo -e "${YELLOW}⚠ Remote changes detected on ${current_branch}.${ENDCOLOR}"
|
|
3720
3910
|
echo
|
|
3721
|
-
echo -e "Do you want to pull ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} first (
|
|
3911
|
+
echo -e "Do you want to pull ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} first (Y/n)?"
|
|
3722
3912
|
read -n 1 -s choice || prompt_aborted
|
|
3723
3913
|
if is_yes "$choice"; then
|
|
3724
3914
|
echo
|
|
@@ -3772,7 +3962,7 @@ function merge_script {
|
|
|
3772
3962
|
elif [ "$local_commit" != "$remote_commit" ]; then
|
|
3773
3963
|
echo -e "${YELLOW}⚠ Remote changes detected.${ENDCOLOR}"
|
|
3774
3964
|
echo
|
|
3775
|
-
echo -e "Do you want to fetch ${YELLOW}${origin_name}/${merge_branch}${ENDCOLOR} before merge (
|
|
3965
|
+
echo -e "Do you want to fetch ${YELLOW}${origin_name}/${merge_branch}${ENDCOLOR} before merge (Y/n)?"
|
|
3776
3966
|
read -n 1 -s choice || prompt_aborted
|
|
3777
3967
|
if is_yes "$choice"; then
|
|
3778
3968
|
echo
|
|
@@ -3901,7 +4091,7 @@ function resolve_conflicts {
|
|
|
3901
4091
|
if [ "$choice" == "4" ]; then
|
|
3902
4092
|
echo
|
|
3903
4093
|
echo -e "${RED}⚠ This will discard your current changes for these files.${ENDCOLOR}"
|
|
3904
|
-
echo -e "Are you sure you want to ${GREEN}accept all incoming changes${ENDCOLOR} (y/
|
|
4094
|
+
echo -e "Are you sure you want to ${GREEN}accept all incoming changes${ENDCOLOR} (y/N)?"
|
|
3905
4095
|
if confirm_destructive; then
|
|
3906
4096
|
echo -e "${YELLOW}Accepting all incoming changes...${ENDCOLOR}"
|
|
3907
4097
|
deleted_files=$(git --no-pager diff --name-only --diff-filter=D --relative 2>/dev/null)
|
|
@@ -3909,7 +4099,7 @@ function resolve_conflicts {
|
|
|
3909
4099
|
echo -e "${YELLOW}⚠ Some files were deleted in the incoming branch:${ENDCOLOR}"
|
|
3910
4100
|
echo "$deleted_files" | sed 's/^/\t/'
|
|
3911
4101
|
echo
|
|
3912
|
-
echo -e "Do you want to continue and accept the deletions (y/
|
|
4102
|
+
echo -e "Do you want to continue and accept the deletions (y/N)?"
|
|
3913
4103
|
if ! confirm_destructive; then
|
|
3914
4104
|
echo -e "${YELLOW}Cancelled. Continuing...${ENDCOLOR}"
|
|
3915
4105
|
continue
|
|
@@ -3967,7 +4157,7 @@ function resolve_conflicts {
|
|
|
3967
4157
|
if [ "$choice" == "5" ]; then
|
|
3968
4158
|
echo
|
|
3969
4159
|
echo -e "${RED}⚠ This will discard the incoming changes for these files.${ENDCOLOR}"
|
|
3970
|
-
echo -e "Are you sure you want to ${GREEN}accept all current changes${ENDCOLOR} (y/
|
|
4160
|
+
echo -e "Are you sure you want to ${GREEN}accept all current changes${ENDCOLOR} (y/N)?"
|
|
3971
4161
|
if confirm_destructive; then
|
|
3972
4162
|
echo -e "${YELLOW}Accepting all current changes...${ENDCOLOR}"
|
|
3973
4163
|
deleted_files=$(git --no-pager diff --name-only --diff-filter=D --relative 2>/dev/null)
|
|
@@ -3975,7 +4165,7 @@ function resolve_conflicts {
|
|
|
3975
4165
|
echo -e "${YELLOW}⚠ Some files were deleted in the current branch:${ENDCOLOR}"
|
|
3976
4166
|
echo "$deleted_files" | sed 's/^/\t/'
|
|
3977
4167
|
echo
|
|
3978
|
-
echo -e "Do you want to continue and accept the deletions (y/
|
|
4168
|
+
echo -e "Do you want to continue and accept the deletions (y/N)?"
|
|
3979
4169
|
if ! confirm_destructive; then
|
|
3980
4170
|
echo -e "${YELLOW}Cancelled. Continuing...${ENDCOLOR}"
|
|
3981
4171
|
continue
|
|
@@ -4094,7 +4284,7 @@ ${staged_with_tab}
|
|
|
4094
4284
|
echo
|
|
4095
4285
|
echo -e "${YELLOW}⚠ Merge commit message cannot be empty.${ENDCOLOR}"
|
|
4096
4286
|
echo
|
|
4097
|
-
read -n 1 -p "Do you want to try for one more time? (
|
|
4287
|
+
read -n 1 -p "Do you want to try for one more time? (Y/n) " -s -e choice || choice="n"
|
|
4098
4288
|
if ! is_yes "$choice"; then
|
|
4099
4289
|
while IFS= read -r _cf; do
|
|
4100
4290
|
[ -n "$_cf" ] && git restore --staged -- ":(literal)$_cf" 2>/dev/null
|
|
@@ -4181,7 +4371,7 @@ function rebase_script {
|
|
|
4181
4371
|
elif [ "$current_local_commit" != "$current_remote_commit" ]; then
|
|
4182
4372
|
echo -e "${YELLOW}⚠ Remote changes detected on ${current_branch}.${ENDCOLOR}"
|
|
4183
4373
|
echo
|
|
4184
|
-
echo -e "Do you want to pull ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} first (
|
|
4374
|
+
echo -e "Do you want to pull ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} first (Y/n)?"
|
|
4185
4375
|
read -n 1 -s choice || prompt_aborted
|
|
4186
4376
|
if is_yes "$choice"; then
|
|
4187
4377
|
echo
|
|
@@ -4340,7 +4530,7 @@ function rebase_branch {
|
|
|
4340
4530
|
fixup_commits_with_dates=$(git log --format="${YELLOW}%h${ENDCOLOR} (${BLUE}%cr${ENDCOLOR}) ${BOLD}%s${ENDCOLOR}" --grep="^fixup!" $ref..HEAD 2>/dev/null || echo "")
|
|
4341
4531
|
echo -e "$fixup_commits_with_dates" | sed 's/^/\t/'
|
|
4342
4532
|
echo
|
|
4343
|
-
echo -e "Proceed with fast autosquash rebase (y/
|
|
4533
|
+
echo -e "Proceed with fast autosquash rebase (y/N)?"
|
|
4344
4534
|
if ! confirm_destructive; then
|
|
4345
4535
|
echo -e "${YELLOW}Rebase cancelled${ENDCOLOR}"
|
|
4346
4536
|
exit
|
|
@@ -4526,7 +4716,7 @@ function rebase_conflicts {
|
|
|
4526
4716
|
if [ "$force_skip" != "true" ]; then
|
|
4527
4717
|
echo
|
|
4528
4718
|
echo -e "${RED}⚠ Dropping this commit will permanently remove it from history.${ENDCOLOR}"
|
|
4529
|
-
echo -e "Are you sure you want to ${RED}skip and discard${ENDCOLOR} this commit (y/
|
|
4719
|
+
echo -e "Are you sure you want to ${RED}skip and discard${ENDCOLOR} this commit (y/N)?"
|
|
4530
4720
|
if ! confirm_destructive; then
|
|
4531
4721
|
echo -e "${YELLOW}Continuing...${ENDCOLOR}"
|
|
4532
4722
|
continue
|
|
@@ -4545,7 +4735,7 @@ function rebase_conflicts {
|
|
|
4545
4735
|
fi
|
|
4546
4736
|
if [ "$choice" == "4" ]; then
|
|
4547
4737
|
echo
|
|
4548
|
-
echo -e "Are you sure you want to ${YELLOW}abort rebase${ENDCOLOR} (y/
|
|
4738
|
+
echo -e "Are you sure you want to ${YELLOW}abort rebase${ENDCOLOR} (y/N)?"
|
|
4549
4739
|
if confirm_destructive; then
|
|
4550
4740
|
echo -e "${YELLOW}Aborting rebase...${ENDCOLOR}"
|
|
4551
4741
|
git rebase --abort
|
|
@@ -4558,7 +4748,7 @@ function rebase_conflicts {
|
|
|
4558
4748
|
if [ "$choice" == "5" ]; then
|
|
4559
4749
|
echo
|
|
4560
4750
|
echo -e "${RED}⚠ This will discard your current changes for these files.${ENDCOLOR}"
|
|
4561
|
-
echo -e "Are you sure you want to ${GREEN}accept all incoming changes${ENDCOLOR} (y/
|
|
4751
|
+
echo -e "Are you sure you want to ${GREEN}accept all incoming changes${ENDCOLOR} (y/N)?"
|
|
4562
4752
|
if confirm_destructive; then
|
|
4563
4753
|
echo -e "${YELLOW}Accepting all incoming changes...${ENDCOLOR}"
|
|
4564
4754
|
deleted_files=$(git --no-pager diff --name-only --diff-filter=D --relative 2>/dev/null)
|
|
@@ -4566,7 +4756,7 @@ function rebase_conflicts {
|
|
|
4566
4756
|
echo -e "${YELLOW}⚠ Some files were deleted in the incoming branch:${ENDCOLOR}"
|
|
4567
4757
|
echo "$deleted_files" | sed 's/^/\t/'
|
|
4568
4758
|
echo
|
|
4569
|
-
echo -e "Do you want to continue and accept the deletions (y/
|
|
4759
|
+
echo -e "Do you want to continue and accept the deletions (y/N)?"
|
|
4570
4760
|
if ! confirm_destructive; then
|
|
4571
4761
|
echo -e "${YELLOW}Cancelled. Continuing...${ENDCOLOR}"
|
|
4572
4762
|
continue
|
|
@@ -4626,7 +4816,7 @@ function rebase_conflicts {
|
|
|
4626
4816
|
if [ "$choice" == "6" ]; then
|
|
4627
4817
|
echo
|
|
4628
4818
|
echo -e "${RED}⚠ This will discard the incoming changes for these files.${ENDCOLOR}"
|
|
4629
|
-
echo -e "Are you sure you want to ${GREEN}accept all current changes${ENDCOLOR} (y/
|
|
4819
|
+
echo -e "Are you sure you want to ${GREEN}accept all current changes${ENDCOLOR} (y/N)?"
|
|
4630
4820
|
if confirm_destructive; then
|
|
4631
4821
|
echo -e "${YELLOW}Accepting all current changes...${ENDCOLOR}"
|
|
4632
4822
|
deleted_files=$(git --no-pager diff --name-only --diff-filter=D --relative 2>/dev/null)
|
|
@@ -4634,7 +4824,7 @@ function rebase_conflicts {
|
|
|
4634
4824
|
echo -e "${YELLOW}⚠ Some files were deleted in the current branch:${ENDCOLOR}"
|
|
4635
4825
|
echo "$deleted_files" | sed 's/^/\t/'
|
|
4636
4826
|
echo
|
|
4637
|
-
echo -e "Do you want to continue and accept the deletions (y/
|
|
4827
|
+
echo -e "Do you want to continue and accept the deletions (y/N)?"
|
|
4638
4828
|
if ! confirm_destructive; then
|
|
4639
4829
|
echo -e "${YELLOW}Cancelled. Continuing...${ENDCOLOR}"
|
|
4640
4830
|
continue
|
|
@@ -4928,7 +5118,7 @@ function handle_cherry_pick_conflicts {
|
|
|
4928
5118
|
;;
|
|
4929
5119
|
2)
|
|
4930
5120
|
echo
|
|
4931
|
-
echo -e "Are you sure you want to ${RED}skip${ENDCOLOR} this commit (y/
|
|
5121
|
+
echo -e "Are you sure you want to ${RED}skip${ENDCOLOR} this commit (y/N)?"
|
|
4932
5122
|
if confirm_destructive; then
|
|
4933
5123
|
echo -e "${YELLOW}Skipping commit ${commit_hash::7}${ENDCOLOR}"
|
|
4934
5124
|
git cherry-pick --skip 2>/dev/null
|
|
@@ -4940,7 +5130,7 @@ function handle_cherry_pick_conflicts {
|
|
|
4940
5130
|
;;
|
|
4941
5131
|
3)
|
|
4942
5132
|
echo
|
|
4943
|
-
echo -e "Are you sure you want to ${GREEN}accept all incoming changes${ENDCOLOR} (y/
|
|
5133
|
+
echo -e "Are you sure you want to ${GREEN}accept all incoming changes${ENDCOLOR} (y/N)?"
|
|
4944
5134
|
if confirm_destructive; then
|
|
4945
5135
|
echo -e "${YELLOW}Accepting all incoming changes...${ENDCOLOR}"
|
|
4946
5136
|
git checkout --theirs . 2>/dev/null
|
|
@@ -4961,7 +5151,7 @@ function handle_cherry_pick_conflicts {
|
|
|
4961
5151
|
;;
|
|
4962
5152
|
4)
|
|
4963
5153
|
echo
|
|
4964
|
-
echo -e "Are you sure you want to ${GREEN}accept all current changes${ENDCOLOR} (y/
|
|
5154
|
+
echo -e "Are you sure you want to ${GREEN}accept all current changes${ENDCOLOR} (y/N)?"
|
|
4965
5155
|
if confirm_destructive; then
|
|
4966
5156
|
echo -e "${YELLOW}Accepting all current changes...${ENDCOLOR}"
|
|
4967
5157
|
git checkout --ours . 2>/dev/null
|
|
@@ -4982,7 +5172,7 @@ function handle_cherry_pick_conflicts {
|
|
|
4982
5172
|
;;
|
|
4983
5173
|
5)
|
|
4984
5174
|
echo
|
|
4985
|
-
echo -e "Are you sure you want to ${YELLOW}abort${ENDCOLOR} the pull operation (y/
|
|
5175
|
+
echo -e "Are you sure you want to ${YELLOW}abort${ENDCOLOR} the pull operation (y/N)?"
|
|
4986
5176
|
if confirm_destructive; then
|
|
4987
5177
|
echo -e "${YELLOW}Aborting pull commits operation...${ENDCOLOR}"
|
|
4988
5178
|
git cherry-pick --abort 2>/dev/null
|
|
@@ -5570,7 +5760,7 @@ function squash_script {
|
|
|
5570
5760
|
size_warning=" ${RED}(large — model accuracy may degrade above ~${SQUASH_MAX_COMMITS} commits)${ENDCOLOR}"
|
|
5571
5761
|
fi
|
|
5572
5762
|
echo -e "${YELLOW}Will analyze ${BOLD}${commit_count}${NORMAL}${YELLOW} commits with AI.${ENDCOLOR}${size_warning}"
|
|
5573
|
-
echo -e "Continue (
|
|
5763
|
+
echo -e "Continue (Y/n)?"
|
|
5574
5764
|
local pre_choice
|
|
5575
5765
|
read -n 1 -s pre_choice || prompt_aborted
|
|
5576
5766
|
if ! is_yes "$pre_choice"; then
|
|
@@ -5620,7 +5810,7 @@ function squash_script {
|
|
|
5620
5810
|
if [ -z "$mode_yes" ]; then
|
|
5621
5811
|
echo -e "${RED}⚠ Applying this plan rewrites your commit history.${ENDCOLOR}"
|
|
5622
5812
|
echo -e "${CYAN}💡 Recover the original branch with ${BOLD}gitb undo rebase${NORMAL}${CYAN} if needed.${ENDCOLOR}"
|
|
5623
|
-
echo -e "Are you sure you want to proceed (y/
|
|
5813
|
+
echo -e "Are you sure you want to proceed (y/N)?"
|
|
5624
5814
|
if ! confirm_destructive; then
|
|
5625
5815
|
echo -e "${YELLOW}Cancelled.${ENDCOLOR}"
|
|
5626
5816
|
exit 0
|
|
@@ -6166,7 +6356,7 @@ function push_script {
|
|
|
6166
6356
|
echo -e "${BLUE}Branch ${YELLOW}${current_branch}${BLUE} does not exist on ${YELLOW}${origin_name}${BLUE} yet.${ENDCOLOR}"
|
|
6167
6357
|
echo
|
|
6168
6358
|
if [ -z "${fast}" ]; then
|
|
6169
|
-
echo -e "Do you want to push the new branch to ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} (
|
|
6359
|
+
echo -e "Do you want to push the new branch to ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} (Y/n)?"
|
|
6170
6360
|
yes_no_choice "Pushing..."
|
|
6171
6361
|
else
|
|
6172
6362
|
echo -e "${YELLOW}Pushing...${ENDCOLOR}"
|
|
@@ -6229,7 +6419,7 @@ function push_script {
|
|
|
6229
6419
|
if [ "${current_branch}" == "${main_branch}" ]; then
|
|
6230
6420
|
echo -e "${RED}⚠ You are about to push directly to the default branch ${YELLOW}${main_branch}${RED}.${ENDCOLOR}"
|
|
6231
6421
|
fi
|
|
6232
|
-
echo -e "Do you want to push${RED}${force_label}${ENDCOLOR} these commits to ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} (
|
|
6422
|
+
echo -e "Do you want to push${RED}${force_label}${ENDCOLOR} these commits to ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} (Y/n)?"
|
|
6233
6423
|
yes_no_choice "Pushing..."
|
|
6234
6424
|
else
|
|
6235
6425
|
echo -e "${YELLOW}Pushing...${ENDCOLOR}"
|
|
@@ -6243,7 +6433,7 @@ function push_script {
|
|
|
6243
6433
|
echo
|
|
6244
6434
|
pull "$current_branch" "$origin_name" "$editor" "rebase"
|
|
6245
6435
|
else
|
|
6246
|
-
echo -e "Do you want to pull ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} (
|
|
6436
|
+
echo -e "Do you want to pull ${YELLOW}${origin_name}/${current_branch}${ENDCOLOR} (Y/n)?"
|
|
6247
6437
|
yes_no_choice "Pulling..."
|
|
6248
6438
|
pull "$current_branch" "$origin_name" "$editor"
|
|
6249
6439
|
fi
|
|
@@ -7225,6 +7415,19 @@ function handle_ai_commit_generation {
|
|
|
7225
7415
|
if [ $? -ne 0 ] || [ -z "$ai_commit_message" ]; then
|
|
7226
7416
|
echo
|
|
7227
7417
|
echo -e "${RED}✗ Cannot generate AI commit message.${ENDCOLOR}"
|
|
7418
|
+
echo
|
|
7419
|
+
read_key choice "Continue with a manual commit message? (Y/n) " || choice="n"
|
|
7420
|
+
echo
|
|
7421
|
+
if is_yes "$choice"; then
|
|
7422
|
+
if [ "$ai_mode" = "subject" ]; then
|
|
7423
|
+
echo
|
|
7424
|
+
fi
|
|
7425
|
+
echo -e "${YELLOW}Falling back to manual commit message creation...${ENDCOLOR}"
|
|
7426
|
+
if [ "$ai_mode" = "subject" ]; then
|
|
7427
|
+
echo
|
|
7428
|
+
fi
|
|
7429
|
+
return
|
|
7430
|
+
fi
|
|
7228
7431
|
cleanup_on_exit "$git_add"
|
|
7229
7432
|
exit 1
|
|
7230
7433
|
fi
|
|
@@ -7289,7 +7492,7 @@ ${ai_commit_message}"
|
|
|
7289
7492
|
echo
|
|
7290
7493
|
echo -e "${YELLOW}⚠ Commit message cannot be empty.${ENDCOLOR}"
|
|
7291
7494
|
echo
|
|
7292
|
-
read -n 1 -p "Try again? (
|
|
7495
|
+
read -n 1 -p "Try again? (Y/n) " -s -e choice || choice="n"
|
|
7293
7496
|
if ! is_yes "$choice"; then
|
|
7294
7497
|
cleanup_on_exit "$git_add"
|
|
7295
7498
|
rm -f "$commitmsg_file"
|
|
@@ -7696,7 +7899,7 @@ function commit_script {
|
|
|
7696
7899
|
if [ -n "$saved_git_add" ]; then
|
|
7697
7900
|
echo
|
|
7698
7901
|
echo -e "${YELLOW}Found previous git add arguments:${ENDCOLOR} ${BOLD}$saved_git_add${ENDCOLOR}"
|
|
7699
|
-
read_key choice "Use them? (
|
|
7902
|
+
read_key choice "Use them? (Y/n) " || choice="n"
|
|
7700
7903
|
echo
|
|
7701
7904
|
if is_yes "$choice"; then
|
|
7702
7905
|
git add $saved_git_add
|
|
@@ -8087,7 +8290,7 @@ ${staged_with_tab}
|
|
|
8087
8290
|
echo
|
|
8088
8291
|
echo -e "${YELLOW}⚠ Commit message cannot be empty.${ENDCOLOR}"
|
|
8089
8292
|
echo
|
|
8090
|
-
read -n 1 -p "Try again? (
|
|
8293
|
+
read -n 1 -p "Try again? (Y/n) " -s -e choice || choice="n"
|
|
8091
8294
|
if ! is_yes "$choice"; then
|
|
8092
8295
|
cleanup_on_exit "$git_add"
|
|
8093
8296
|
rm -f "$commitmsg_file"
|
|
@@ -8293,7 +8496,7 @@ function edit_branch_name {
|
|
|
8293
8496
|
if [ "$old_name" = "$main_branch" ]; then
|
|
8294
8497
|
echo -e "${YELLOW}⚠ ${BOLD}${old_name}${NORMAL} is the configured main branch.${ENDCOLOR}"
|
|
8295
8498
|
echo -e "Renaming it will leave ${YELLOW}gitbasher.branch${ENDCOLOR} pointing at the old name."
|
|
8296
|
-
printf "Rename anyway? (y/
|
|
8499
|
+
printf "Rename anyway? (y/N) "
|
|
8297
8500
|
yes_no_choice_strict
|
|
8298
8501
|
echo
|
|
8299
8502
|
fi
|
|
@@ -8316,7 +8519,7 @@ function edit_branch_name {
|
|
|
8316
8519
|
echo
|
|
8317
8520
|
echo -e "${YELLOW}${BOLD}${old_name}${NORMAL}${YELLOW} also exists on ${origin_name}.${ENDCOLOR}"
|
|
8318
8521
|
echo -e "Push ${BOLD}${new_name}${NORMAL} and delete the old remote branch?"
|
|
8319
|
-
printf "(y/
|
|
8522
|
+
printf "(y/N) "
|
|
8320
8523
|
yes_no_choice_strict
|
|
8321
8524
|
echo
|
|
8322
8525
|
local push_out
|
|
@@ -8481,7 +8684,7 @@ function branch_script {
|
|
|
8481
8684
|
done
|
|
8482
8685
|
echo
|
|
8483
8686
|
echo -e "${RED}⚠ These branches will be force-deleted (${BLUE}git branch -D${RED}).${ENDCOLOR}"
|
|
8484
|
-
echo -e "Are you sure you want to delete them (y/
|
|
8687
|
+
echo -e "Are you sure you want to delete them (y/N)?"
|
|
8485
8688
|
yes_no_choice_strict "Deleting gone branches..."
|
|
8486
8689
|
for branch in "${gone_branches[@]}"; do
|
|
8487
8690
|
delete_output=$(git branch -D "$branch" 2>&1)
|
|
@@ -8501,7 +8704,7 @@ function branch_script {
|
|
|
8501
8704
|
fi
|
|
8502
8705
|
if [[ -n "${tag}" ]]; then
|
|
8503
8706
|
echo -e "${YELLOW}Do you want to fetch remote tags first?${ENDCOLOR}"
|
|
8504
|
-
read -n 1 -p "Fetch remote? (
|
|
8707
|
+
read -n 1 -p "Fetch remote? (Y/n) " choice || choice="n"
|
|
8505
8708
|
echo
|
|
8506
8709
|
if [ "$choice" = "y" ] || [ "$choice" = "Y" ]; then
|
|
8507
8710
|
echo
|
|
@@ -8584,7 +8787,7 @@ function branch_script {
|
|
|
8584
8787
|
elif [[ -z "$new" ]] && [[ -n "$delete" ]] && [[ -z "$tag" ]]; then
|
|
8585
8788
|
echo -e "${YELLOW}Clean up local branches that no longer exist on the remote?${ENDCOLOR}"
|
|
8586
8789
|
echo
|
|
8587
|
-
printf "Continue (
|
|
8790
|
+
printf "Continue (Y/n)? "
|
|
8588
8791
|
while [ true ]; do
|
|
8589
8792
|
read -n 1 -s choice || prompt_aborted
|
|
8590
8793
|
if is_yes "$choice"; then
|
|
@@ -8614,7 +8817,7 @@ function branch_script {
|
|
|
8614
8817
|
done
|
|
8615
8818
|
echo
|
|
8616
8819
|
echo -e "${RED}⚠ These branches will be force-deleted (${BLUE}git branch -D${RED}).${ENDCOLOR}"
|
|
8617
|
-
printf "Are you sure you want to delete them (y/
|
|
8820
|
+
printf "Are you sure you want to delete them (y/N)? "
|
|
8618
8821
|
if confirm_destructive; then
|
|
8619
8822
|
echo
|
|
8620
8823
|
for branch in "${branches_to_delete[@]}"; do
|
|
@@ -8654,7 +8857,7 @@ function branch_script {
|
|
|
8654
8857
|
do
|
|
8655
8858
|
printf "\t${merged_branches_without_main[index]}\n"
|
|
8656
8859
|
done
|
|
8657
|
-
printf "\nDelete these merged branches (
|
|
8860
|
+
printf "\nDelete these merged branches (Y/n)? "
|
|
8658
8861
|
while [ true ]; do
|
|
8659
8862
|
read -n 1 -s choice || prompt_aborted
|
|
8660
8863
|
if is_yes "$choice"; then
|
|
@@ -8722,7 +8925,7 @@ function branch_script {
|
|
|
8722
8925
|
done
|
|
8723
8926
|
echo
|
|
8724
8927
|
echo -e "${RED}⚠ These will be force-deleted (${BLUE}git branch -D${RED}).${ENDCOLOR}"
|
|
8725
|
-
printf "Are you sure (y/
|
|
8928
|
+
printf "Are you sure (y/N)? "
|
|
8726
8929
|
if confirm_destructive; then
|
|
8727
8930
|
echo
|
|
8728
8931
|
for branch in "${squash_merged_branches[@]}"; do
|
|
@@ -8750,7 +8953,7 @@ function branch_script {
|
|
|
8750
8953
|
elif [[ ${delete_output} == *"is not fully merged"* ]]; then
|
|
8751
8954
|
echo -e "${YELLOW}⚠ Branch '$branch_name' is not fully merged.${ENDCOLOR}"
|
|
8752
8955
|
echo -e "${RED}⚠ Force-delete (${BLUE}git branch -D${RED}) will discard unmerged commits.${ENDCOLOR}"
|
|
8753
|
-
printf "Force-delete this branch (y/
|
|
8956
|
+
printf "Force-delete this branch (y/N)? "
|
|
8754
8957
|
confirm_destructive
|
|
8755
8958
|
case $? in
|
|
8756
8959
|
0)
|
|
@@ -8774,7 +8977,7 @@ function branch_script {
|
|
|
8774
8977
|
fi
|
|
8775
8978
|
if git rev-parse --verify --quiet "refs/remotes/$origin_name/$branch_name" >/dev/null 2>&1; then
|
|
8776
8979
|
echo
|
|
8777
|
-
printf "Also delete this branch on ${origin_name} (y/
|
|
8980
|
+
printf "Also delete this branch on ${origin_name} (y/N)? "
|
|
8778
8981
|
if confirm_destructive; then
|
|
8779
8982
|
echo
|
|
8780
8983
|
echo -e "${YELLOW}Deleting from remote...${ENDCOLOR}"
|
|
@@ -8945,7 +9148,7 @@ function branch_script {
|
|
|
8945
9148
|
fi
|
|
8946
9149
|
if [ -n "$origin_name" ]; then
|
|
8947
9150
|
echo
|
|
8948
|
-
echo -e "Do you want to push the new branch to ${YELLOW}${origin_name}/${branch_name}${ENDCOLOR} (
|
|
9151
|
+
echo -e "Do you want to push the new branch to ${YELLOW}${origin_name}/${branch_name}${ENDCOLOR} (Y/n)?"
|
|
8949
9152
|
yes_no_choice "Pushing..."
|
|
8950
9153
|
current_branch="$branch_name"
|
|
8951
9154
|
push -u
|
|
@@ -9184,7 +9387,7 @@ function tag_script {
|
|
|
9184
9387
|
fi
|
|
9185
9388
|
if [ -n "${delete}" ] && [ -z "$select" ]; then
|
|
9186
9389
|
echo
|
|
9187
|
-
echo -e "${YELLOW}Do you really want to delete all local tags (y/
|
|
9390
|
+
echo -e "${YELLOW}Do you really want to delete all local tags (y/N)?${ENDCOLOR}"
|
|
9188
9391
|
yes_no_choice_strict "\nDeleting all local tags..."
|
|
9189
9392
|
local _da_failed=0
|
|
9190
9393
|
while IFS= read -r _da_tag; do
|
|
@@ -9225,7 +9428,7 @@ function tag_script {
|
|
|
9225
9428
|
check_code $? "$delete_result" "delete tag"
|
|
9226
9429
|
echo -e "${GREEN}✓ Deleted tag '${tag_name}'${ENDCOLOR}"
|
|
9227
9430
|
echo
|
|
9228
|
-
echo -e "Also delete this tag on ${YELLOW}${origin_name}${ENDCOLOR} (y/
|
|
9431
|
+
echo -e "Also delete this tag on ${YELLOW}${origin_name}${ENDCOLOR} (y/N)?"
|
|
9229
9432
|
yes_no_choice_strict "\nDeleting..."
|
|
9230
9433
|
push_tag $tag_name "true"
|
|
9231
9434
|
exit
|
|
@@ -9286,7 +9489,7 @@ function tag_script {
|
|
|
9286
9489
|
echo
|
|
9287
9490
|
echo -e "${YELLOW}⚠ Tag message cannot be empty.${ENDCOLOR}"
|
|
9288
9491
|
echo
|
|
9289
|
-
read -n 1 -p "Try again? (
|
|
9492
|
+
read -n 1 -p "Try again? (Y/n) " -s -e choice || choice="n"
|
|
9290
9493
|
if ! is_yes "$choice"; then
|
|
9291
9494
|
rm -f "$tag_file"
|
|
9292
9495
|
exit
|
|
@@ -9324,7 +9527,7 @@ function tag_script {
|
|
|
9324
9527
|
echo -e "$tag_message"
|
|
9325
9528
|
fi
|
|
9326
9529
|
echo
|
|
9327
|
-
echo -e "Do you want to push it to ${YELLOW}${origin_name}${ENDCOLOR} (
|
|
9530
|
+
echo -e "Do you want to push it to ${YELLOW}${origin_name}${ENDCOLOR} (Y/n)?"
|
|
9328
9531
|
yes_no_choice "\nPushing..."
|
|
9329
9532
|
push_tag $tag_name
|
|
9330
9533
|
}
|
|
@@ -9418,7 +9621,7 @@ function reset_script {
|
|
|
9418
9621
|
else
|
|
9419
9622
|
echo -e "${YELLOW}⚠ This will move HEAD; reset changes will be left unstaged.${ENDCOLOR}"
|
|
9420
9623
|
fi
|
|
9421
|
-
echo -e "Are you sure you want to continue (y/
|
|
9624
|
+
echo -e "Are you sure you want to continue (y/N)?"
|
|
9422
9625
|
yes_no_choice_strict "Resetting..."
|
|
9423
9626
|
reset_output=$(git reset "$args" "$move_ref" 2>&1)
|
|
9424
9627
|
check_code $? "$reset_output" "reset"
|
|
@@ -9714,7 +9917,7 @@ function stash_script {
|
|
|
9714
9917
|
fi
|
|
9715
9918
|
echo
|
|
9716
9919
|
echo -e "${RED}⚠ Dropping stash $selected_stash cannot be undone.${ENDCOLOR}"
|
|
9717
|
-
echo -e "Are you sure you want to drop it (y/
|
|
9920
|
+
echo -e "Are you sure you want to drop it (y/N)?"
|
|
9718
9921
|
yes_no_choice_strict "Dropping stash"
|
|
9719
9922
|
drop_output=$(git stash drop "$selected_stash" 2>&1)
|
|
9720
9923
|
drop_code=$?
|
|
@@ -9829,7 +10032,7 @@ function cherry_script {
|
|
|
9829
10032
|
echo -e "Files with changes:"
|
|
9830
10033
|
git status --porcelain | sed 's/^/ /'
|
|
9831
10034
|
echo
|
|
9832
|
-
echo -e "Continue with cherry-pick (may cause conflicts) (
|
|
10035
|
+
echo -e "Continue with cherry-pick (may cause conflicts) (Y/n)?"
|
|
9833
10036
|
yes_no_choice "Proceeding..."
|
|
9834
10037
|
echo
|
|
9835
10038
|
fi
|
|
@@ -9909,7 +10112,7 @@ function handle_cherry_pick_conflicts {
|
|
|
9909
10112
|
fi
|
|
9910
10113
|
if [[ $output == *"The previous cherry-pick is now empty"* ]]; then
|
|
9911
10114
|
echo -e "${YELLOW}This commit is empty after cherry-pick (changes already applied).${ENDCOLOR}"
|
|
9912
|
-
echo -e "Skip it (y/
|
|
10115
|
+
echo -e "Skip it (y/N)?"
|
|
9913
10116
|
yes_no_choice_strict "Skipping empty commit..."
|
|
9914
10117
|
skip_output=$(git cherry-pick --skip 2>&1)
|
|
9915
10118
|
if [ $? == 0 ]; then
|
|
@@ -10029,7 +10232,7 @@ function choose_commits_interactive {
|
|
|
10029
10232
|
echo -e " ${commit::7} - $message"
|
|
10030
10233
|
done
|
|
10031
10234
|
echo
|
|
10032
|
-
echo -e "Proceed with cherry-pick? (
|
|
10235
|
+
echo -e "Proceed with cherry-pick? (Y/n)"
|
|
10033
10236
|
yes_no_choice "Proceeding with cherry-pick..."
|
|
10034
10237
|
local success_count=0
|
|
10035
10238
|
local total_count=${#selected_commits[@]}
|
|
@@ -10272,7 +10475,7 @@ function undo_commit {
|
|
|
10272
10475
|
echo -e "\t$cancelled_commit"
|
|
10273
10476
|
echo
|
|
10274
10477
|
echo -e "This will undo the last commit but ${GREEN}keep all changes staged${ENDCOLOR}"
|
|
10275
|
-
echo -e "Do you want to continue (y/
|
|
10478
|
+
echo -e "Do you want to continue (y/N)?"
|
|
10276
10479
|
yes_no_choice_strict "Undoing commit..."
|
|
10277
10480
|
reset_output=$(git reset --soft HEAD~1 2>&1)
|
|
10278
10481
|
reset_code=$?
|
|
@@ -10321,7 +10524,7 @@ function undo_amend {
|
|
|
10321
10524
|
echo -e "${YELLOW}Current (amended):${ENDCOLOR}\t$current_commit"
|
|
10322
10525
|
echo -e "${GREEN}Restore to:${ENDCOLOR}\t\t$pre_amend_commit"
|
|
10323
10526
|
echo
|
|
10324
|
-
echo -e "Do you want to undo the amend (y/
|
|
10527
|
+
echo -e "Do you want to undo the amend (y/N)?"
|
|
10325
10528
|
yes_no_choice_strict "Undoing amend..."
|
|
10326
10529
|
reset_output=$(git reset --soft "$pre_amend_ref" 2>&1)
|
|
10327
10530
|
reset_code=$?
|
|
@@ -10341,7 +10544,7 @@ function undo_amend {
|
|
|
10341
10544
|
function undo_merge {
|
|
10342
10545
|
if [ -f "$(git rev-parse --git-dir)/MERGE_HEAD" ]; then
|
|
10343
10546
|
echo -e "${YELLOW}⚠ A merge is in progress.${ENDCOLOR}"
|
|
10344
|
-
echo -e "Abort the ongoing merge (y/
|
|
10547
|
+
echo -e "Abort the ongoing merge (y/N)?"
|
|
10345
10548
|
yes_no_choice_strict "Aborting merge..."
|
|
10346
10549
|
abort_output=$(git merge --abort 2>&1)
|
|
10347
10550
|
abort_code=$?
|
|
@@ -10370,7 +10573,7 @@ function undo_merge {
|
|
|
10370
10573
|
echo -e "${GREEN}Restore to:${ENDCOLOR}\t$orig_commit"
|
|
10371
10574
|
echo
|
|
10372
10575
|
echo -e "${RED}⚠ This will discard the merge commit and all merge changes.${ENDCOLOR}"
|
|
10373
|
-
echo -e "Are you sure you want to undo the merge (y/
|
|
10576
|
+
echo -e "Are you sure you want to undo the merge (y/N)?"
|
|
10374
10577
|
yes_no_choice_strict "Undoing merge..."
|
|
10375
10578
|
reset_output=$(git reset --merge ORIG_HEAD 2>&1)
|
|
10376
10579
|
reset_code=$?
|
|
@@ -10387,7 +10590,7 @@ function undo_merge {
|
|
|
10387
10590
|
function undo_rebase {
|
|
10388
10591
|
if [ -d "$(git rev-parse --git-dir)/rebase-merge" ] || [ -d "$(git rev-parse --git-dir)/rebase-apply" ]; then
|
|
10389
10592
|
echo -e "${YELLOW}⚠ A rebase is in progress.${ENDCOLOR}"
|
|
10390
|
-
echo -e "Abort the ongoing rebase (y/
|
|
10593
|
+
echo -e "Abort the ongoing rebase (y/N)?"
|
|
10391
10594
|
yes_no_choice_strict "Aborting rebase..."
|
|
10392
10595
|
abort_output=$(git rebase --abort 2>&1)
|
|
10393
10596
|
abort_code=$?
|
|
@@ -10418,7 +10621,7 @@ function undo_rebase {
|
|
|
10418
10621
|
exit 1
|
|
10419
10622
|
fi
|
|
10420
10623
|
echo -e "${RED}⚠ This will discard all rebase changes and restore the original branch state.${ENDCOLOR}"
|
|
10421
|
-
echo -e "Are you sure you want to undo the rebase (y/
|
|
10624
|
+
echo -e "Are you sure you want to undo the rebase (y/N)?"
|
|
10422
10625
|
yes_no_choice_strict "Undoing rebase..."
|
|
10423
10626
|
reset_output=$(git reset --hard ORIG_HEAD 2>&1)
|
|
10424
10627
|
reset_code=$?
|
|
@@ -10442,7 +10645,7 @@ function undo_stash {
|
|
|
10442
10645
|
git_status
|
|
10443
10646
|
echo
|
|
10444
10647
|
echo -e "This will stash all current changes to undo the last stash pop/apply."
|
|
10445
|
-
echo -e "Continue (y/
|
|
10648
|
+
echo -e "Continue (y/N)?"
|
|
10446
10649
|
yes_no_choice_strict "Re-stashing changes..."
|
|
10447
10650
|
stash_output=$(git stash push -m "undo: re-stashed changes" --include-untracked 2>&1)
|
|
10448
10651
|
stash_code=$?
|
|
@@ -11492,7 +11695,7 @@ function log_commit_actions {
|
|
|
11492
11695
|
choice=$((10#$choice))
|
|
11493
11696
|
local file="${files[choice - 1]}"
|
|
11494
11697
|
echo -e "${RED}This will overwrite '${file}' in your working tree with its state from ${hash}.${ENDCOLOR}"
|
|
11495
|
-
printf "Restore it (y/
|
|
11698
|
+
printf "Restore it (y/N)? "
|
|
11496
11699
|
if confirm_destructive; then
|
|
11497
11700
|
result=$(git checkout "$hash" -- "$file" 2>&1)
|
|
11498
11701
|
check_code $? "$result" "restore"
|
|
@@ -12709,7 +12912,7 @@ function _do_worktree_remove {
|
|
|
12709
12912
|
echo -e "${RED}${rm_output}${ENDCOLOR}"
|
|
12710
12913
|
echo
|
|
12711
12914
|
echo -e "${YELLOW}Force remove this worktree? (uncommitted changes will be lost)${ENDCOLOR}"
|
|
12712
|
-
echo -e "Do you want to continue (y/
|
|
12915
|
+
echo -e "Do you want to continue (y/N)?"
|
|
12713
12916
|
yes_no_choice_strict "Force removing worktree..."
|
|
12714
12917
|
rm_output=$(LC_ALL=C git worktree remove --force --force "$selected_worktree_path" 2>&1)
|
|
12715
12918
|
rm_code=$?
|
|
@@ -12742,7 +12945,7 @@ function worktree_prune {
|
|
|
12742
12945
|
echo -e "${YELLOW}Records that would be removed:${ENDCOLOR}"
|
|
12743
12946
|
echo "$dry_run"
|
|
12744
12947
|
echo
|
|
12745
|
-
echo -e "${YELLOW}Continue? (
|
|
12948
|
+
echo -e "${YELLOW}Continue? (Y/n)${ENDCOLOR}"
|
|
12746
12949
|
yes_no_choice "Pruning..."
|
|
12747
12950
|
local prune_output
|
|
12748
12951
|
prune_output=$(git worktree prune --verbose 2>&1)
|
|
@@ -13422,7 +13625,7 @@ function create_hook {
|
|
|
13422
13625
|
if [ -f "$hook_file" ]; then
|
|
13423
13626
|
echo -e "${YELLOW}⚠ Hook '$hook_type' already exists.${ENDCOLOR}"
|
|
13424
13627
|
echo -e "${RED}⚠ Overwriting will replace its contents.${ENDCOLOR}"
|
|
13425
|
-
echo -e "Overwrite (y/
|
|
13628
|
+
echo -e "Overwrite (y/N)?"
|
|
13426
13629
|
if ! confirm_destructive; then
|
|
13427
13630
|
echo -e "${YELLOW}Hook creation cancelled.${ENDCOLOR}"
|
|
13428
13631
|
return 1
|
|
@@ -13468,7 +13671,7 @@ fi
|
|
|
13468
13671
|
# lines for any binary blob that happens to be staged.
|
|
13469
13672
|
if git diff --cached --name-only -z | xargs -0 grep -l --binary-files=without-match \"TODO\\|FIXME\" 2>/dev/null; then
|
|
13470
13673
|
echo \"⚠️ Warning: Found TODO/FIXME comments in staged files\"
|
|
13471
|
-
echo \"Continue anyway? (y/
|
|
13674
|
+
echo \"Continue anyway? (y/N)\"
|
|
13472
13675
|
read -n 1 answer
|
|
13473
13676
|
echo
|
|
13474
13677
|
if [ \"\$answer\" != \"y\" ] && [ \"\$answer\" != \"Y\" ]; then
|
|
@@ -13573,7 +13776,7 @@ exit 0"
|
|
|
13573
13776
|
echo -e "${GREEN}✓ Created executable hook: $hook_type${ENDCOLOR}"
|
|
13574
13777
|
echo -e "${YELLOW}Hook file: $hook_file${ENDCOLOR}"
|
|
13575
13778
|
echo
|
|
13576
|
-
echo -e "Do you want to ${BLUE}edit${ENDCOLOR} the hook now? (
|
|
13779
|
+
echo -e "Do you want to ${BLUE}edit${ENDCOLOR} the hook now? (Y/n)"
|
|
13577
13780
|
read -n 1 -s choice || prompt_aborted
|
|
13578
13781
|
echo
|
|
13579
13782
|
if is_yes "$choice"; then
|
|
@@ -13644,7 +13847,7 @@ function remove_hook {
|
|
|
13644
13847
|
fi
|
|
13645
13848
|
echo -e "${RED}⚠ Deleting hook '$hook_type' cannot be undone.${ENDCOLOR}"
|
|
13646
13849
|
echo -e "${GRAY}File: $hook_file${ENDCOLOR}"
|
|
13647
|
-
echo -e "Are you sure you want to delete it (y/
|
|
13850
|
+
echo -e "Are you sure you want to delete it (y/N)?"
|
|
13648
13851
|
if confirm_destructive; then
|
|
13649
13852
|
rm "$hook_file"
|
|
13650
13853
|
echo -e "${GREEN}✓ Removed hook: $hook_type${ENDCOLOR}"
|
|
@@ -13685,7 +13888,7 @@ function remove_all_hooks {
|
|
|
13685
13888
|
fi
|
|
13686
13889
|
echo
|
|
13687
13890
|
echo -e "${RED}⚠ This will permanently delete $hook_count hooks.${ENDCOLOR}"
|
|
13688
|
-
echo -e "${YELLOW}Are you absolutely sure (
|
|
13891
|
+
echo -e "${YELLOW}Are you absolutely sure (Y/n)?${ENDCOLOR}"
|
|
13689
13892
|
read -n 1 -p "Your choice: " choice < /dev/tty || choice="0"
|
|
13690
13893
|
echo
|
|
13691
13894
|
echo
|
|
@@ -13816,7 +14019,7 @@ function install_samples {
|
|
|
13816
14019
|
fi
|
|
13817
14020
|
echo
|
|
13818
14021
|
echo -e "${YELLOW}Ready to install $sample_count sample hooks.${ENDCOLOR}"
|
|
13819
|
-
echo -e "Continue (
|
|
14022
|
+
echo -e "Continue (Y/n)?"
|
|
13820
14023
|
read -n 1 -p "Your choice: " choice < /dev/tty || choice="n"
|
|
13821
14024
|
echo
|
|
13822
14025
|
echo
|
|
@@ -14264,7 +14467,7 @@ function origin_remove {
|
|
|
14264
14467
|
echo -e "URL: ${GRAY}$existing${ENDCOLOR}"
|
|
14265
14468
|
echo
|
|
14266
14469
|
echo -e "${RED}⚠ This will remove the remote (you can re-add it later).${ENDCOLOR}"
|
|
14267
|
-
echo -e "Are you sure (y/
|
|
14470
|
+
echo -e "Are you sure (y/N)?"
|
|
14268
14471
|
yes_no_choice_strict "" "true"
|
|
14269
14472
|
git remote remove "$target"
|
|
14270
14473
|
echo
|
|
@@ -14423,7 +14626,7 @@ function clone_script {
|
|
|
14423
14626
|
echo
|
|
14424
14627
|
if [ -z "$GITBASHER_TEST_MODE" ] && [ -t 0 ]; then
|
|
14425
14628
|
local clone_cd_choice
|
|
14426
|
-
echo -e "${YELLOW}Enter the new repo now (
|
|
14629
|
+
echo -e "${YELLOW}Enter the new repo now (Y/n)?${ENDCOLOR}"
|
|
14427
14630
|
read -n 1 -s clone_cd_choice
|
|
14428
14631
|
echo
|
|
14429
14632
|
if is_yes "$clone_cd_choice"; then
|
|
@@ -14765,9 +14968,9 @@ function update_script {
|
|
|
14765
14968
|
fi
|
|
14766
14969
|
echo -e "Install location: ${BLUE}${install_path}${ENDCOLOR}"
|
|
14767
14970
|
if [ $cmp -eq 2 ]; then
|
|
14768
|
-
echo -e "Update ${YELLOW}${current_version}${ENDCOLOR} → ${GREEN}${latest_version}${ENDCOLOR}? (
|
|
14971
|
+
echo -e "Update ${YELLOW}${current_version}${ENDCOLOR} → ${GREEN}${latest_version}${ENDCOLOR}? (Y/n)"
|
|
14769
14972
|
else
|
|
14770
|
-
echo -e "Reinstall ${GREEN}${latest_version}${ENDCOLOR} over the current binary? (
|
|
14973
|
+
echo -e "Reinstall ${GREEN}${latest_version}${ENDCOLOR} over the current binary? (Y/n)"
|
|
14771
14974
|
fi
|
|
14772
14975
|
yes_no_choice "Updating..."
|
|
14773
14976
|
local tmp_path
|
|
@@ -15321,7 +15524,7 @@ _gitb() {
|
|
|
15321
15524
|
_values 'tag mode' 'annotated[create annotated tag]' 'commit[tag a commit]' 'all[tag everything]' 'push[push tag]' 'push-all[push all tags]' 'delete[delete tag]' 'delete-all[delete all]' 'list[list tags]' 'remote[remote tags]' 'help[show help]'
|
|
15322
15525
|
;;
|
|
15323
15526
|
config|cf|cfg|conf)
|
|
15324
|
-
_values 'config key' 'default[default branch]' 'separator[separator char]' 'editor[editor]' 'ticket[ticket prefix]' 'scopes[scope list]' 'ai[AI key]' 'provider[AI provider]' 'model[AI model]' 'proxy[AI proxy]' 'history[AI history]' 'diff[AI diff payload]' 'push-size[warn on large pushes]' 'delete[unset a key]' 'user[user name/email]' 'auto[manage tab completion]' 'help[show help]'
|
|
15527
|
+
_values 'config key' 'default[default branch]' 'separator[separator char]' 'editor[editor]' 'ticket[ticket prefix]' 'scopes[scope list]' 'ai[AI setup wizard]' 'key[AI API key]' 'provider[AI provider]' 'model[AI model]' 'proxy[AI proxy]' 'history[AI history]' 'diff[AI diff payload]' 'push-size[warn on large pushes]' 'delete[unset a key]' 'user[user name/email]' 'auto[manage tab completion]' 'help[show help]'
|
|
15325
15528
|
;;
|
|
15326
15529
|
undo|un)
|
|
15327
15530
|
_values 'undo target' 'commit[undo last commit]' 'amend[undo amend]' 'merge[undo merge]' 'rebase[undo rebase]' 'stash[undo stash]' 'help[show help]'
|
|
@@ -15523,7 +15726,7 @@ complete -c gitb -n "$__gitb_branch" -a "list remote main new newd delete prev r
|
|
|
15523
15726
|
set -l __gitb_tag "__gitb_using_cmd tag t tg; and __gitb_at_position 2"
|
|
15524
15727
|
complete -c gitb -n "$__gitb_tag" -a "annotated commit all push push-all delete delete-all list remote help"
|
|
15525
15728
|
set -l __gitb_config "__gitb_using_cmd config cf cfg conf; and __gitb_at_position 2"
|
|
15526
|
-
complete -c gitb -n "$__gitb_config" -a "default separator editor ticket scopes ai provider model proxy history diff push-size delete user auto help"
|
|
15729
|
+
complete -c gitb -n "$__gitb_config" -a "default separator editor ticket scopes ai key provider model proxy history diff push-size delete user auto help"
|
|
15527
15730
|
set -l __gitb_undo "__gitb_using_cmd undo un; and __gitb_at_position 2"
|
|
15528
15731
|
complete -c gitb -n "$__gitb_undo" -a "commit amend merge rebase stash help"
|
|
15529
15732
|
set -l __gitb_reset "__gitb_using_cmd reset res; and __gitb_at_position 2"
|