gitbash 1.9.1 → 2.0.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/README.md +100 -89
- package/bin/gitbash +345 -723
- package/commands/_utils.sh +485 -157
- package/commands/branch.sh +9 -12
- package/commands/cleanstash.sh +47 -122
- package/commands/cleanup.sh +305 -441
- package/commands/commit.sh +136 -172
- package/commands/commits.sh +81 -114
- package/commands/create.sh +231 -314
- package/commands/pr.sh +158 -146
- package/commands/stale.sh +137 -277
- package/commands/stash.sh +11 -39
- package/commands/stashes.sh +10 -13
- package/commands/status.sh +197 -250
- package/commands/switch.sh +186 -274
- package/commands/unstash.sh +45 -109
- package/commands/update.sh +123 -142
- package/docs/CONTRIBUTING.md +16 -5
- package/docs/SECURITY.md +14 -3
- package/package.json +5 -3
package/bin/gitbash
CHANGED
|
@@ -1,753 +1,370 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# gitbash - Interactive git utilities for bash with fzf-powered menus
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# Resolve symlinks to get the actual script location
|
|
5
|
+
SOURCE="${BASH_SOURCE[0]}"
|
|
6
|
+
while [[ -L "$SOURCE" ]]; do
|
|
7
|
+
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
8
|
+
SOURCE="$(readlink "$SOURCE")"
|
|
9
|
+
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
|
|
10
|
+
done
|
|
11
|
+
SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
|
|
12
|
+
GITBASH_BIN="$SCRIPT_DIR/bin/gitbash"
|
|
13
|
+
COMMANDS_DIR="$SCRIPT_DIR/commands"
|
|
14
|
+
VERSION="$(grep -o '"version": *"[^"]*"' "$SCRIPT_DIR/package.json" | head -1 | cut -d'"' -f4)"
|
|
15
|
+
export SCRIPT_DIR GITBASH_BIN VERSION
|
|
5
16
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
USER_CONFIG_FILE=".gitbashrc-user"
|
|
17
|
+
# shellcheck source=../commands/_utils.sh
|
|
18
|
+
source "$COMMANDS_DIR/_utils.sh"
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
local user_config="$repo_root/$USER_CONFIG_FILE"
|
|
28
|
-
if [[ -f "$user_config" ]]; then
|
|
29
|
-
# shellcheck disable=SC1090
|
|
30
|
-
source "$user_config"
|
|
31
|
-
fi
|
|
32
|
-
fi
|
|
33
|
-
}
|
|
20
|
+
# Available commands
|
|
21
|
+
COMMANDS=(
|
|
22
|
+
"branch"
|
|
23
|
+
"cleanstash"
|
|
24
|
+
"cleanup"
|
|
25
|
+
"commit"
|
|
26
|
+
"commits"
|
|
27
|
+
"create"
|
|
28
|
+
"pr"
|
|
29
|
+
"stale"
|
|
30
|
+
"stash"
|
|
31
|
+
"stashes"
|
|
32
|
+
"status"
|
|
33
|
+
"switch"
|
|
34
|
+
"unstash"
|
|
35
|
+
"update"
|
|
36
|
+
)
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
is_command() {
|
|
39
|
+
local cmd
|
|
40
|
+
for cmd in "${COMMANDS[@]}"; do
|
|
41
|
+
[[ "$cmd" == "$1" ]] && return 0
|
|
42
|
+
done
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
local
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
echo "Configure gitbash (writes to $CONFIG_FILE)"
|
|
57
|
-
echo
|
|
46
|
+
# =============================================================================
|
|
47
|
+
# Configuration wizard
|
|
48
|
+
# =============================================================================
|
|
49
|
+
|
|
50
|
+
# key|type|question
|
|
51
|
+
SETTINGS=(
|
|
52
|
+
"GITBASH_CREATE_BRANCH_PREFIX|prefix|Branch prefix inserted between type and branch name (e.g. 'team' → feature/team/PROJ-123-fix-bug)"
|
|
53
|
+
"GITBASH_CREATE_NO_ISSUE_PARSING|parsing|Parse Jira issue numbers into branch names? (yes/no)"
|
|
54
|
+
"GITBASH_CREATE_ISSUE_PARSING_FALLBACK|text|Fallback issue prefix when no issue number is given"
|
|
55
|
+
"GITBASH_CREATE_AUTO_PUSH|yesno|Push new branches to the remote right after 'create'? (yes/no)"
|
|
56
|
+
"GITBASH_MERGE_COMMAND|command|Merge tool opened by 'update' on conflicts (not allowed in the committed .gitbashrc)"
|
|
57
|
+
"GITBASH_THEME|theme|Theme for delta/bat previews (auto/dark/light)"
|
|
58
|
+
"GITBASH_STALE_MONTHS|number|Stale remote branch threshold in months (used by 'stale')"
|
|
59
|
+
"GITBASH_CLEANUP_DAYS|number|Stale local branch threshold in days (used by 'cleanup')"
|
|
60
|
+
"GITBASH_PROTECTED_BRANCHES|text|Branches never offered for deletion (space-separated globs)"
|
|
61
|
+
"GITBASH_BASE_BRANCH|text|Base branch (empty = detect from the remote's HEAD, then main/master)"
|
|
62
|
+
"GITBASH_REMOTE|text|Remote name"
|
|
63
|
+
)
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
echo "
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if [[ -z "$prefix" ]]; then
|
|
75
|
-
prefix="$current_prefix"
|
|
76
|
-
elif [[ "$prefix" == "-" ]]; then
|
|
77
|
-
prefix=""
|
|
78
|
-
fi
|
|
79
|
-
# Remove trailing slash if present (will be added automatically)
|
|
80
|
-
prefix="${prefix%/}"
|
|
65
|
+
# Default value of a setting
|
|
66
|
+
setting_default() {
|
|
67
|
+
case "$1" in
|
|
68
|
+
GITBASH_CREATE_NO_ISSUE_PARSING) echo "no" ;;
|
|
69
|
+
GITBASH_CREATE_ISSUE_PARSING_FALLBACK) echo "NOISSUE" ;;
|
|
70
|
+
GITBASH_CREATE_AUTO_PUSH) echo "yes" ;;
|
|
71
|
+
GITBASH_MERGE_COMMAND) echo "fork" ;;
|
|
72
|
+
GITBASH_THEME) echo "auto" ;;
|
|
73
|
+
GITBASH_STALE_MONTHS) echo "3" ;;
|
|
74
|
+
GITBASH_CLEANUP_DAYS) echo "7" ;;
|
|
75
|
+
GITBASH_PROTECTED_BRANCHES) echo "main master develop release/*" ;;
|
|
76
|
+
GITBASH_REMOTE) echo "origin" ;;
|
|
77
|
+
*) echo "" ;;
|
|
78
|
+
esac
|
|
79
|
+
}
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
# Value of a setting stored in a specific file. Returns 1 if not set there.
|
|
82
|
+
file_value() {
|
|
83
|
+
local file="$1" key="$2" k v
|
|
84
|
+
while IFS='=' read -r k v; do
|
|
85
|
+
if [[ "$k" == "$key" ]]; then
|
|
86
|
+
printf '%s' "$v"
|
|
87
|
+
return 0
|
|
88
|
+
fi
|
|
89
|
+
done < <(_gb_config_entries "$file")
|
|
90
|
+
return 1
|
|
91
|
+
}
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
local
|
|
91
|
-
if [[ "$
|
|
92
|
-
|
|
93
|
+
# Convert between the stored value and what the wizard shows.
|
|
94
|
+
# GITBASH_CREATE_NO_ISSUE_PARSING is stored inverted.
|
|
95
|
+
to_display() {
|
|
96
|
+
local type="$1" value="$2"
|
|
97
|
+
if [[ "$type" == "parsing" ]]; then
|
|
98
|
+
[[ "$value" == "yes" ]] && echo "no" || echo "yes"
|
|
99
|
+
else
|
|
100
|
+
printf '%s' "$value"
|
|
93
101
|
fi
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Validate an answer and print the value to store. Returns 1 if invalid.
|
|
105
|
+
to_stored() {
|
|
106
|
+
local type="$1" answer="$2"
|
|
107
|
+
if [[ "$answer" == *'"'* || "$answer" == *'$'* || "$answer" == *'`'* || "$answer" == *\\* ]]; then
|
|
108
|
+
echo "Values cannot contain \", \$, \` or \\." >&2
|
|
109
|
+
return 1
|
|
110
|
+
fi
|
|
111
|
+
case "$type" in
|
|
112
|
+
parsing)
|
|
113
|
+
case "$answer" in
|
|
114
|
+
yes) echo "no" ;;
|
|
115
|
+
no) echo "yes" ;;
|
|
116
|
+
*) echo "Please answer yes or no." >&2; return 1 ;;
|
|
117
|
+
esac
|
|
118
|
+
;;
|
|
119
|
+
yesno)
|
|
120
|
+
case "$answer" in
|
|
121
|
+
yes|no) echo "$answer" ;;
|
|
122
|
+
*) echo "Please answer yes or no." >&2; return 1 ;;
|
|
123
|
+
esac
|
|
124
|
+
;;
|
|
125
|
+
theme)
|
|
126
|
+
case "$answer" in
|
|
127
|
+
auto|dark|light) echo "$answer" ;;
|
|
128
|
+
*) echo "Please answer auto, dark or light." >&2; return 1 ;;
|
|
129
|
+
esac
|
|
130
|
+
;;
|
|
131
|
+
number)
|
|
132
|
+
if [[ "$answer" =~ ^[1-9][0-9]*$ ]]; then
|
|
133
|
+
echo "$answer"
|
|
134
|
+
else
|
|
135
|
+
echo "Please enter a whole number of at least 1." >&2
|
|
136
|
+
return 1
|
|
137
|
+
fi
|
|
102
138
|
;;
|
|
103
|
-
|
|
104
|
-
|
|
139
|
+
prefix)
|
|
140
|
+
echo "${answer%/}"
|
|
105
141
|
;;
|
|
106
142
|
*)
|
|
107
|
-
echo "
|
|
108
|
-
no_issue_parsing="no"
|
|
143
|
+
echo "$answer"
|
|
109
144
|
;;
|
|
110
145
|
esac
|
|
146
|
+
}
|
|
111
147
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
*)
|
|
131
|
-
echo "Invalid theme. Using 'auto'."
|
|
132
|
-
theme="auto"
|
|
148
|
+
# Interactive configuration for one scope: global (~/.gitbashrc),
|
|
149
|
+
# local (<repo>/.gitbashrc, committed) or user (<repo>/.gitbashrc-user, not committed)
|
|
150
|
+
configure_wizard() {
|
|
151
|
+
local scope="$1" file repo_root=""
|
|
152
|
+
case "$scope" in
|
|
153
|
+
global)
|
|
154
|
+
file=$(gb_global_config_file)
|
|
155
|
+
;;
|
|
156
|
+
local|user)
|
|
157
|
+
if ! repo_root=$(git rev-parse --show-toplevel 2>/dev/null); then
|
|
158
|
+
print_error "Not inside a git repository."
|
|
159
|
+
return 1
|
|
160
|
+
fi
|
|
161
|
+
if [[ "$scope" == "local" ]]; then
|
|
162
|
+
file="$repo_root/.gitbashrc"
|
|
163
|
+
else
|
|
164
|
+
file="$repo_root/.gitbashrc-user"
|
|
165
|
+
fi
|
|
133
166
|
;;
|
|
134
167
|
esac
|
|
135
168
|
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
read -re -p "Stale branch threshold in months (used by stale) [${current_stale_months}]: " input
|
|
139
|
-
stale_months=${input:-$current_stale_months}
|
|
140
|
-
if ! [[ "$stale_months" =~ ^[0-9]+$ ]] || [[ "$stale_months" -lt 1 ]]; then
|
|
141
|
-
echo "Invalid value. Using default of 3 months."
|
|
142
|
-
stale_months=3
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# Cleanup threshold
|
|
146
|
-
read -re -p "Cleanup threshold in days (used by cleanup) [${current_cleanup_days}]: " input
|
|
147
|
-
cleanup_days=${input:-$current_cleanup_days}
|
|
148
|
-
if ! [[ "$cleanup_days" =~ ^[0-9]+$ ]] || [[ "$cleanup_days" -lt 1 ]]; then
|
|
149
|
-
echo "Invalid value. Using default of 7 days."
|
|
150
|
-
cleanup_days=7
|
|
151
|
-
fi
|
|
152
|
-
|
|
153
|
-
cat > "$CONFIG_FILE" <<EOF
|
|
154
|
-
# Automatically generated gitbash configuration
|
|
155
|
-
|
|
156
|
-
# CAUTION: Do not edit this file manually to avoid setting being overwritten.
|
|
157
|
-
# run 'gitbash --config' to update settings
|
|
169
|
+
# Effective values from all config files, used as defaults
|
|
170
|
+
gb_load_config 2>/dev/null
|
|
158
171
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
GITBASH_CREATE_BRANCH_PREFIX="$prefix"
|
|
163
|
-
|
|
164
|
-
# Merge tool command invoked by 'update' when conflicts occur
|
|
165
|
-
GITBASH_MERGE_COMMAND="$merge_cmd"
|
|
166
|
-
|
|
167
|
-
# Disable Jira issue number parsing in branch names (yes/no)
|
|
168
|
-
GITBASH_CREATE_NO_ISSUE_PARSING="$no_issue_parsing"
|
|
169
|
-
|
|
170
|
-
# Fallback prefix when no issue number is provided (only used if parsing is enabled)
|
|
171
|
-
GITBASH_CREATE_ISSUE_PARSING_FALLBACK="$issue_fallback"
|
|
172
|
-
|
|
173
|
-
# Theme for delta/bat: auto, dark, or light
|
|
174
|
-
GITBASH_THEME="$theme"
|
|
175
|
-
|
|
176
|
-
# Stale branch threshold in months (branches older than this in 'stale')
|
|
177
|
-
GITBASH_STALE_MONTHS="$stale_months"
|
|
178
|
-
|
|
179
|
-
# Cleanup threshold in days (branches merged more than this many days ago)
|
|
180
|
-
GITBASH_CLEANUP_DAYS="$cleanup_days"
|
|
181
|
-
|
|
182
|
-
EOF
|
|
183
|
-
|
|
184
|
-
echo
|
|
185
|
-
echo "Saved configuration to $CONFIG_FILE"
|
|
186
|
-
|
|
187
|
-
# Check for dependencies
|
|
188
|
-
check_dependencies
|
|
189
|
-
|
|
190
|
-
# Check for shell integration
|
|
191
|
-
check_shell_integration
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
configure_local_gitbash() {
|
|
195
|
-
# Check if we're in a git repository
|
|
196
|
-
local repo_root
|
|
197
|
-
if ! repo_root=$(git rev-parse --show-toplevel 2>/dev/null); then
|
|
198
|
-
echo "Error: Not inside a git repository." >&2
|
|
199
|
-
exit 1
|
|
200
|
-
fi
|
|
201
|
-
|
|
202
|
-
local local_config="$repo_root/$LOCAL_CONFIG_FILE"
|
|
203
|
-
|
|
204
|
-
# Load global config first to get current defaults
|
|
205
|
-
load_config
|
|
206
|
-
|
|
207
|
-
# Backward compatibility: migrate GITBASH_FEATURE_BRANCH_PREFIX to GITBASH_CREATE_BRANCH_PREFIX
|
|
208
|
-
local current_prefix="${GITBASH_CREATE_BRANCH_PREFIX:-}"
|
|
209
|
-
if [[ -z "$current_prefix" && -n "${GITBASH_FEATURE_BRANCH_PREFIX:-}" ]]; then
|
|
210
|
-
# Remove "feature/" prefix from old variable
|
|
211
|
-
current_prefix="${GITBASH_FEATURE_BRANCH_PREFIX#feature/}"
|
|
212
|
-
fi
|
|
213
|
-
# Remove trailing slash if present
|
|
214
|
-
current_prefix="${current_prefix%/}"
|
|
215
|
-
|
|
216
|
-
echo "Configure local gitbash overrides for this repository"
|
|
217
|
-
echo "Config will be saved to: $local_config"
|
|
218
|
-
echo
|
|
219
|
-
echo "Press Enter to keep global setting, or enter a new value to override."
|
|
220
|
-
echo "Enter '-' to explicitly unset a local override."
|
|
221
|
-
echo
|
|
222
|
-
|
|
223
|
-
local overrides=()
|
|
224
|
-
local input
|
|
225
|
-
|
|
226
|
-
# Branch prefix (current_prefix already set above with backward compatibility)
|
|
227
|
-
echo "1. Branch prefix"
|
|
228
|
-
echo " Current (global): ${current_prefix:-'(empty)'}"
|
|
229
|
-
read -re -p " Local override: " input
|
|
230
|
-
if [[ -n "$input" ]]; then
|
|
231
|
-
if [[ "$input" == "-" ]]; then
|
|
232
|
-
overrides+=("GITBASH_CREATE_BRANCH_PREFIX=\"\"")
|
|
233
|
-
echo " → Will use empty prefix locally"
|
|
234
|
-
else
|
|
235
|
-
input="${input%/}" # Remove trailing slash
|
|
236
|
-
overrides+=("GITBASH_CREATE_BRANCH_PREFIX=\"$input\"")
|
|
237
|
-
echo " → Will use '$input' locally"
|
|
238
|
-
fi
|
|
239
|
-
else
|
|
240
|
-
echo " → Keeping global setting"
|
|
241
|
-
fi
|
|
242
|
-
echo
|
|
243
|
-
|
|
244
|
-
# Merge command
|
|
245
|
-
local current_merge="${GITBASH_MERGE_COMMAND:-fork}"
|
|
246
|
-
echo "2. Merge tool command (used by update on conflicts)"
|
|
247
|
-
echo " Current (global): $current_merge"
|
|
248
|
-
read -re -p " Local override: " input
|
|
249
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
250
|
-
overrides+=("GITBASH_MERGE_COMMAND=\"$input\"")
|
|
251
|
-
echo " → Will use '$input' locally"
|
|
252
|
-
else
|
|
253
|
-
echo " → Keeping global setting"
|
|
254
|
-
fi
|
|
255
|
-
echo
|
|
256
|
-
|
|
257
|
-
# Issue parsing
|
|
258
|
-
local current_no_issue_parsing="${GITBASH_CREATE_NO_ISSUE_PARSING:-no}"
|
|
259
|
-
local current_enabled="yes"
|
|
260
|
-
[[ "$current_no_issue_parsing" == "yes" ]] && current_enabled="no"
|
|
261
|
-
echo "3. Enable Jira issue parsing in branch names? (yes/no)"
|
|
262
|
-
echo " Current (global): $current_enabled"
|
|
263
|
-
read -re -p " Local override: " input
|
|
264
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
265
|
-
case "$input" in
|
|
266
|
-
yes)
|
|
267
|
-
overrides+=("GITBASH_CREATE_NO_ISSUE_PARSING=\"no\"")
|
|
268
|
-
echo " → Will enable parsing locally"
|
|
269
|
-
;;
|
|
270
|
-
no)
|
|
271
|
-
overrides+=("GITBASH_CREATE_NO_ISSUE_PARSING=\"yes\"")
|
|
272
|
-
echo " → Will disable parsing locally"
|
|
273
|
-
;;
|
|
274
|
-
*)
|
|
275
|
-
echo " → Invalid value, keeping global setting"
|
|
276
|
-
;;
|
|
277
|
-
esac
|
|
278
|
-
else
|
|
279
|
-
echo " → Keeping global setting"
|
|
280
|
-
fi
|
|
281
|
-
echo
|
|
282
|
-
|
|
283
|
-
# Issue fallback
|
|
284
|
-
local current_issue_fallback="${GITBASH_CREATE_ISSUE_PARSING_FALLBACK:-NOISSUE}"
|
|
285
|
-
echo "4. Fallback prefix when no issue number is provided"
|
|
286
|
-
echo " Current (global): $current_issue_fallback"
|
|
287
|
-
read -re -p " Local override: " input
|
|
288
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
289
|
-
overrides+=("GITBASH_CREATE_ISSUE_PARSING_FALLBACK=\"$input\"")
|
|
290
|
-
echo " → Will use '$input' locally"
|
|
172
|
+
echo "Configure gitbash ($scope) - writes to $file"
|
|
173
|
+
if [[ "$scope" == "global" ]]; then
|
|
174
|
+
echo "Press Enter to keep the current value, or '-' to reset it to the default."
|
|
291
175
|
else
|
|
292
|
-
echo "
|
|
176
|
+
echo "Press Enter to keep this file's current setting, '-' to remove the override,"
|
|
177
|
+
echo "or '\"\"' to override with an empty value."
|
|
293
178
|
fi
|
|
294
179
|
echo
|
|
295
180
|
|
|
296
|
-
|
|
297
|
-
local
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
;;
|
|
310
|
-
esac
|
|
311
|
-
else
|
|
312
|
-
echo " → Keeping global setting"
|
|
313
|
-
fi
|
|
314
|
-
echo
|
|
315
|
-
|
|
316
|
-
# Stale months
|
|
317
|
-
local current_stale_months="${GITBASH_STALE_MONTHS:-3}"
|
|
318
|
-
echo "6. Stale branch threshold in months"
|
|
319
|
-
echo " Current (global): $current_stale_months"
|
|
320
|
-
read -re -p " Local override: " input
|
|
321
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
322
|
-
if [[ "$input" =~ ^[0-9]+$ ]] && [[ "$input" -ge 1 ]]; then
|
|
323
|
-
overrides+=("GITBASH_STALE_MONTHS=\"$input\"")
|
|
324
|
-
echo " → Will use '$input' locally"
|
|
325
|
-
else
|
|
326
|
-
echo " → Invalid value, keeping global setting"
|
|
181
|
+
local -a lines
|
|
182
|
+
local entry key type question current default answer stored has_override shown n=0
|
|
183
|
+
for entry in "${SETTINGS[@]}"; do
|
|
184
|
+
n=$((n + 1))
|
|
185
|
+
IFS='|' read -r key type question <<< "$entry"
|
|
186
|
+
[[ "$scope" == "local" && "$key" == "GITBASH_MERGE_COMMAND" ]] && continue
|
|
187
|
+
|
|
188
|
+
default=$(setting_default "$key")
|
|
189
|
+
has_override=false
|
|
190
|
+
if current=$(file_value "$file" "$key"); then
|
|
191
|
+
has_override=true
|
|
192
|
+
elif [[ "$scope" == "global" ]]; then
|
|
193
|
+
current="$default"
|
|
327
194
|
fi
|
|
328
|
-
else
|
|
329
|
-
echo " → Keeping global setting"
|
|
330
|
-
fi
|
|
331
|
-
echo
|
|
332
195
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
echo " Current (global): $current_cleanup_days"
|
|
337
|
-
read -re -p " Local override: " input
|
|
338
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
339
|
-
if [[ "$input" =~ ^[0-9]+$ ]] && [[ "$input" -ge 1 ]]; then
|
|
340
|
-
overrides+=("GITBASH_CLEANUP_DAYS=\"$input\"")
|
|
341
|
-
echo " → Will use '$input' locally"
|
|
196
|
+
echo "$n. $question"
|
|
197
|
+
if [[ "$scope" == "global" ]]; then
|
|
198
|
+
shown=$(to_display "$type" "$current")
|
|
342
199
|
else
|
|
343
|
-
|
|
200
|
+
shown=$(to_display "$type" "${!key}")
|
|
201
|
+
if [[ "$has_override" == true ]]; then
|
|
202
|
+
echo " Current override: '$(to_display "$type" "$current")'"
|
|
203
|
+
else
|
|
204
|
+
echo " Current (inherited): '$shown'"
|
|
205
|
+
fi
|
|
344
206
|
fi
|
|
345
|
-
else
|
|
346
|
-
echo " → Keeping global setting"
|
|
347
|
-
fi
|
|
348
|
-
echo
|
|
349
|
-
|
|
350
|
-
# Check if any overrides were set
|
|
351
|
-
if [[ ${#overrides[@]} -eq 0 ]]; then
|
|
352
|
-
echo "No local overrides configured."
|
|
353
|
-
return 0
|
|
354
|
-
fi
|
|
355
207
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
208
|
+
while true; do
|
|
209
|
+
if ! prompt_read " Value [$shown]: " answer; then
|
|
210
|
+
echo "Aborted - nothing was saved."
|
|
211
|
+
return 1
|
|
212
|
+
fi
|
|
213
|
+
if [[ -z "$answer" ]]; then
|
|
214
|
+
# Keep what this file has
|
|
215
|
+
if [[ "$scope" == "global" || "$has_override" == true ]]; then
|
|
216
|
+
lines+=("$key=\"$current\"")
|
|
217
|
+
fi
|
|
218
|
+
break
|
|
219
|
+
fi
|
|
220
|
+
if [[ "$answer" == "-" ]]; then
|
|
221
|
+
if [[ "$scope" == "global" ]]; then
|
|
222
|
+
lines+=("$key=\"$default\"")
|
|
223
|
+
fi
|
|
224
|
+
break
|
|
225
|
+
fi
|
|
226
|
+
[[ "$answer" == '""' ]] && answer=""
|
|
227
|
+
if [[ -z "$answer" && "$type" != "prefix" && "$type" != "text" ]]; then
|
|
228
|
+
echo " This setting cannot be empty." >&2
|
|
229
|
+
continue
|
|
230
|
+
fi
|
|
231
|
+
if stored=$(to_stored "$type" "$answer"); then
|
|
232
|
+
lines+=("$key=\"$stored\"")
|
|
233
|
+
break
|
|
234
|
+
fi
|
|
364
235
|
done
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
echo "Saved local configuration to $local_config"
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
configure_user_gitbash() {
|
|
371
|
-
# Check if we're in a git repository
|
|
372
|
-
local repo_root
|
|
373
|
-
if ! repo_root=$(git rev-parse --show-toplevel 2>/dev/null); then
|
|
374
|
-
echo "Error: Not inside a git repository." >&2
|
|
375
|
-
exit 1
|
|
376
|
-
fi
|
|
377
|
-
|
|
378
|
-
local user_config="$repo_root/$USER_CONFIG_FILE"
|
|
379
|
-
local gitignore="$repo_root/.gitignore"
|
|
380
|
-
|
|
381
|
-
# Load all configs to get current effective values
|
|
382
|
-
load_config
|
|
383
|
-
|
|
384
|
-
# Backward compatibility: migrate GITBASH_FEATURE_BRANCH_PREFIX to GITBASH_CREATE_BRANCH_PREFIX
|
|
385
|
-
local current_prefix="${GITBASH_CREATE_BRANCH_PREFIX:-}"
|
|
386
|
-
if [[ -z "$current_prefix" && -n "${GITBASH_FEATURE_BRANCH_PREFIX:-}" ]]; then
|
|
387
|
-
# Remove "feature/" prefix from old variable
|
|
388
|
-
current_prefix="${GITBASH_FEATURE_BRANCH_PREFIX#feature/}"
|
|
389
|
-
fi
|
|
390
|
-
# Remove trailing slash if present
|
|
391
|
-
current_prefix="${current_prefix%/}"
|
|
392
|
-
|
|
393
|
-
echo "Configure user-specific gitbash overrides for this repository"
|
|
394
|
-
echo "Config will be saved to: $user_config"
|
|
395
|
-
echo "This file is intended for personal settings and excluded from version control."
|
|
396
|
-
echo
|
|
397
|
-
echo "Press Enter to keep current setting, or enter a new value to override."
|
|
398
|
-
echo "Enter '-' to explicitly unset an override."
|
|
399
|
-
echo
|
|
400
|
-
|
|
401
|
-
local overrides=()
|
|
402
|
-
local input
|
|
403
|
-
|
|
404
|
-
# Branch prefix
|
|
405
|
-
echo "1. Branch prefix"
|
|
406
|
-
echo " Current (effective): ${current_prefix:-'(empty)'}"
|
|
407
|
-
read -re -p " User override: " input
|
|
408
|
-
if [[ -n "$input" ]]; then
|
|
409
|
-
if [[ "$input" == "-" ]]; then
|
|
410
|
-
overrides+=("GITBASH_CREATE_BRANCH_PREFIX=\"\"")
|
|
411
|
-
echo " → Will use empty prefix"
|
|
412
|
-
else
|
|
413
|
-
input="${input%/}" # Remove trailing slash
|
|
414
|
-
overrides+=("GITBASH_CREATE_BRANCH_PREFIX=\"$input\"")
|
|
415
|
-
echo " → Will use '$input'"
|
|
416
|
-
fi
|
|
417
|
-
else
|
|
418
|
-
echo " → Keeping current setting"
|
|
419
|
-
fi
|
|
420
|
-
echo
|
|
421
|
-
|
|
422
|
-
# Merge command
|
|
423
|
-
local current_merge="${GITBASH_MERGE_COMMAND:-fork}"
|
|
424
|
-
echo "2. Merge tool command (used by update on conflicts)"
|
|
425
|
-
echo " Current (effective): $current_merge"
|
|
426
|
-
read -re -p " User override: " input
|
|
427
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
428
|
-
overrides+=("GITBASH_MERGE_COMMAND=\"$input\"")
|
|
429
|
-
echo " → Will use '$input'"
|
|
430
|
-
else
|
|
431
|
-
echo " → Keeping current setting"
|
|
432
|
-
fi
|
|
433
|
-
echo
|
|
434
|
-
|
|
435
|
-
# Issue parsing
|
|
436
|
-
local current_no_issue_parsing="${GITBASH_CREATE_NO_ISSUE_PARSING:-no}"
|
|
437
|
-
local current_enabled="yes"
|
|
438
|
-
[[ "$current_no_issue_parsing" == "yes" ]] && current_enabled="no"
|
|
439
|
-
echo "3. Enable Jira issue parsing in branch names? (yes/no)"
|
|
440
|
-
echo " Current (effective): $current_enabled"
|
|
441
|
-
read -re -p " User override: " input
|
|
442
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
443
|
-
case "$input" in
|
|
444
|
-
yes)
|
|
445
|
-
overrides+=("GITBASH_CREATE_NO_ISSUE_PARSING=\"no\"")
|
|
446
|
-
echo " → Will enable parsing"
|
|
447
|
-
;;
|
|
448
|
-
no)
|
|
449
|
-
overrides+=("GITBASH_CREATE_NO_ISSUE_PARSING=\"yes\"")
|
|
450
|
-
echo " → Will disable parsing"
|
|
451
|
-
;;
|
|
452
|
-
*)
|
|
453
|
-
echo " → Invalid value, keeping current setting"
|
|
454
|
-
;;
|
|
455
|
-
esac
|
|
456
|
-
else
|
|
457
|
-
echo " → Keeping current setting"
|
|
458
|
-
fi
|
|
459
|
-
echo
|
|
460
|
-
|
|
461
|
-
# Issue fallback
|
|
462
|
-
local current_issue_fallback="${GITBASH_CREATE_ISSUE_PARSING_FALLBACK:-NOISSUE}"
|
|
463
|
-
echo "4. Fallback prefix when no issue number is provided"
|
|
464
|
-
echo " Current (effective): $current_issue_fallback"
|
|
465
|
-
read -re -p " User override: " input
|
|
466
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
467
|
-
overrides+=("GITBASH_CREATE_ISSUE_PARSING_FALLBACK=\"$input\"")
|
|
468
|
-
echo " → Will use '$input'"
|
|
469
|
-
else
|
|
470
|
-
echo " → Keeping current setting"
|
|
471
|
-
fi
|
|
472
|
-
echo
|
|
473
|
-
|
|
474
|
-
# Theme
|
|
475
|
-
local current_theme="${GITBASH_THEME:-auto}"
|
|
476
|
-
echo "5. Theme for delta/bat (auto/dark/light)"
|
|
477
|
-
echo " Current (effective): $current_theme"
|
|
478
|
-
read -re -p " User override: " input
|
|
479
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
480
|
-
case "$input" in
|
|
481
|
-
auto|dark|light)
|
|
482
|
-
overrides+=("GITBASH_THEME=\"$input\"")
|
|
483
|
-
echo " → Will use '$input'"
|
|
484
|
-
;;
|
|
485
|
-
*)
|
|
486
|
-
echo " → Invalid value, keeping current setting"
|
|
487
|
-
;;
|
|
488
|
-
esac
|
|
489
|
-
else
|
|
490
|
-
echo " → Keeping current setting"
|
|
491
|
-
fi
|
|
492
|
-
echo
|
|
493
|
-
|
|
494
|
-
# Stale months
|
|
495
|
-
local current_stale_months="${GITBASH_STALE_MONTHS:-3}"
|
|
496
|
-
echo "6. Stale branch threshold in months"
|
|
497
|
-
echo " Current (effective): $current_stale_months"
|
|
498
|
-
read -re -p " User override: " input
|
|
499
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
500
|
-
if [[ "$input" =~ ^[0-9]+$ ]] && [[ "$input" -ge 1 ]]; then
|
|
501
|
-
overrides+=("GITBASH_STALE_MONTHS=\"$input\"")
|
|
502
|
-
echo " → Will use '$input'"
|
|
503
|
-
else
|
|
504
|
-
echo " → Invalid value, keeping current setting"
|
|
505
|
-
fi
|
|
506
|
-
else
|
|
507
|
-
echo " → Keeping current setting"
|
|
508
|
-
fi
|
|
509
|
-
echo
|
|
236
|
+
echo
|
|
237
|
+
done
|
|
510
238
|
|
|
511
|
-
#
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
read -re -p " User override: " input
|
|
516
|
-
if [[ -n "$input" && "$input" != "-" ]]; then
|
|
517
|
-
if [[ "$input" =~ ^[0-9]+$ ]] && [[ "$input" -ge 1 ]]; then
|
|
518
|
-
overrides+=("GITBASH_CLEANUP_DAYS=\"$input\"")
|
|
519
|
-
echo " → Will use '$input'"
|
|
239
|
+
if [[ "$scope" != "global" && ${#lines[@]} -eq 0 ]]; then
|
|
240
|
+
if [[ -f "$file" ]]; then
|
|
241
|
+
rm -f "$file"
|
|
242
|
+
print_info "No overrides left - removed $file"
|
|
520
243
|
else
|
|
521
|
-
|
|
244
|
+
print_info "No overrides configured."
|
|
522
245
|
fi
|
|
523
|
-
else
|
|
524
|
-
echo " → Keeping current setting"
|
|
525
|
-
fi
|
|
526
|
-
echo
|
|
527
|
-
|
|
528
|
-
# Check if any overrides were set
|
|
529
|
-
if [[ ${#overrides[@]} -eq 0 ]]; then
|
|
530
|
-
echo "No user overrides configured."
|
|
531
246
|
return 0
|
|
532
247
|
fi
|
|
533
248
|
|
|
534
|
-
|
|
249
|
+
local flag="--config"
|
|
250
|
+
[[ "$scope" != "global" ]] && flag="--config-$scope"
|
|
535
251
|
{
|
|
536
|
-
echo "#
|
|
537
|
-
echo "#
|
|
538
|
-
echo "#
|
|
539
|
-
echo "# This file should NOT be committed to version control"
|
|
252
|
+
echo "# gitbash configuration ($scope). Generated by 'gitbash $flag'."
|
|
253
|
+
echo "# Only plain GITBASH_*=\"value\" lines are read; nothing in this file is executed."
|
|
254
|
+
[[ "$scope" == "user" ]] && echo "# Personal settings - not committed (listed in .git/info/exclude)."
|
|
540
255
|
echo
|
|
541
|
-
|
|
542
|
-
|
|
256
|
+
local line
|
|
257
|
+
for line in ${lines[@]+"${lines[@]}"}; do
|
|
258
|
+
echo "$line"
|
|
543
259
|
done
|
|
544
|
-
} > "$
|
|
545
|
-
|
|
546
|
-
echo "Saved user configuration to $user_config"
|
|
547
|
-
|
|
548
|
-
# Add to .gitignore if not already present
|
|
549
|
-
if [[ -f "$gitignore" ]]; then
|
|
550
|
-
if ! grep -qxF "$USER_CONFIG_FILE" "$gitignore"; then
|
|
551
|
-
echo "$USER_CONFIG_FILE" >> "$gitignore"
|
|
552
|
-
echo "Added '$USER_CONFIG_FILE' to .gitignore"
|
|
553
|
-
else
|
|
554
|
-
echo "'$USER_CONFIG_FILE' already in .gitignore"
|
|
555
|
-
fi
|
|
556
|
-
else
|
|
557
|
-
echo "$USER_CONFIG_FILE" > "$gitignore"
|
|
558
|
-
echo "Created .gitignore with '$USER_CONFIG_FILE'"
|
|
559
|
-
fi
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
# Check and offer to install dependencies
|
|
563
|
-
check_dependencies() {
|
|
564
|
-
local missing_required=()
|
|
565
|
-
local missing_optional=()
|
|
260
|
+
} > "$file"
|
|
261
|
+
print_success "Saved $file"
|
|
566
262
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
# Optional dependencies
|
|
573
|
-
if ! command -v delta >/dev/null 2>&1; then
|
|
574
|
-
missing_optional+=("git-delta")
|
|
575
|
-
fi
|
|
576
|
-
if ! command -v bat >/dev/null 2>&1; then
|
|
577
|
-
missing_optional+=("bat")
|
|
263
|
+
if [[ "$scope" == "user" ]]; then
|
|
264
|
+
exclude_user_config
|
|
265
|
+
elif [[ "$scope" == "global" ]]; then
|
|
266
|
+
check_dependencies
|
|
267
|
+
check_shell_integration
|
|
578
268
|
fi
|
|
269
|
+
}
|
|
579
270
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
271
|
+
# Keep .gitbashrc-user out of git without touching the tracked .gitignore
|
|
272
|
+
exclude_user_config() {
|
|
273
|
+
local exclude
|
|
274
|
+
exclude=$(git rev-parse --git-path info/exclude 2>/dev/null) || return 0
|
|
275
|
+
mkdir -p "$(dirname "$exclude")"
|
|
276
|
+
if [[ -f "$exclude" ]] && grep -qxF ".gitbashrc-user" "$exclude"; then
|
|
584
277
|
return 0
|
|
585
278
|
fi
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
# Show status of each dependency
|
|
591
|
-
if command -v fzf >/dev/null 2>&1; then
|
|
592
|
-
echo " ✓ fzf (optional)"
|
|
593
|
-
else
|
|
594
|
-
echo " ✗ fzf (optional) - fuzzy finder for interactive selection"
|
|
595
|
-
fi
|
|
596
|
-
|
|
597
|
-
if command -v delta >/dev/null 2>&1; then
|
|
598
|
-
echo " ✓ git-delta (optional)"
|
|
599
|
-
else
|
|
600
|
-
echo " ✗ git-delta (optional) - better diff highlighting"
|
|
601
|
-
fi
|
|
602
|
-
|
|
603
|
-
if command -v bat >/dev/null 2>&1; then
|
|
604
|
-
echo " ✓ bat (optional)"
|
|
605
|
-
else
|
|
606
|
-
echo " ✗ bat (optional) - syntax highlighting for file previews"
|
|
279
|
+
# Make sure we start on a new line
|
|
280
|
+
if [[ -s "$exclude" && -n "$(tail -c 1 "$exclude")" ]]; then
|
|
281
|
+
echo >> "$exclude"
|
|
607
282
|
fi
|
|
283
|
+
echo ".gitbashrc-user" >> "$exclude"
|
|
284
|
+
print_info "Added .gitbashrc-user to $exclude"
|
|
285
|
+
}
|
|
608
286
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
287
|
+
# Report missing optional dependencies and offer to install them with Homebrew
|
|
288
|
+
check_dependencies() {
|
|
289
|
+
local missing=() dep
|
|
290
|
+
command -v fzf >/dev/null 2>&1 || missing+=("fzf")
|
|
291
|
+
command -v delta >/dev/null 2>&1 || missing+=("git-delta")
|
|
292
|
+
command -v bat >/dev/null 2>&1 || command -v batcat >/dev/null 2>&1 || missing+=("bat")
|
|
613
293
|
|
|
614
|
-
|
|
615
|
-
if [[ ${#
|
|
294
|
+
echo
|
|
295
|
+
if [[ ${#missing[@]} -eq 0 ]]; then
|
|
296
|
+
if require_fzf; then
|
|
297
|
+
print_success "All dependencies installed."
|
|
298
|
+
fi
|
|
616
299
|
return 0
|
|
617
300
|
fi
|
|
618
301
|
|
|
619
|
-
|
|
302
|
+
echo "Missing optional dependencies: ${missing[*]}"
|
|
303
|
+
echo " fzf (>= $GB_FZF_MIN_VERSION) - interactive menus, git-delta - diff highlighting, bat - file previews"
|
|
620
304
|
if ! command -v brew >/dev/null 2>&1; then
|
|
621
|
-
echo
|
|
622
|
-
echo "Homebrew not found. Install dependencies manually:"
|
|
623
|
-
for dep in "${all_missing[@]}"; do
|
|
624
|
-
echo " brew install $dep"
|
|
625
|
-
done
|
|
305
|
+
echo "Install them with your package manager, e.g.: sudo apt install ${missing[*]}"
|
|
626
306
|
return 0
|
|
627
307
|
fi
|
|
308
|
+
if gb_confirm "Install them with Homebrew?" y; then
|
|
309
|
+
for dep in "${missing[@]}"; do
|
|
310
|
+
if brew install "$dep"; then
|
|
311
|
+
print_success "$dep installed"
|
|
312
|
+
else
|
|
313
|
+
print_error "Failed to install $dep"
|
|
314
|
+
fi
|
|
315
|
+
done
|
|
316
|
+
fi
|
|
317
|
+
}
|
|
628
318
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
local
|
|
632
|
-
|
|
633
|
-
case "$
|
|
634
|
-
|
|
635
|
-
|
|
319
|
+
# Offer to add the shell integration to the rc file of the user's shell
|
|
320
|
+
check_shell_integration() {
|
|
321
|
+
local shell_name shell_rc
|
|
322
|
+
shell_name=$(basename "${SHELL:-}")
|
|
323
|
+
case "$shell_name" in
|
|
324
|
+
zsh) shell_rc="$HOME/.zshrc" ;;
|
|
325
|
+
bash)
|
|
326
|
+
if [[ -f "$HOME/.bashrc" ]]; then
|
|
327
|
+
shell_rc="$HOME/.bashrc"
|
|
328
|
+
else
|
|
329
|
+
shell_rc="$HOME/.bash_profile"
|
|
330
|
+
fi
|
|
636
331
|
;;
|
|
637
332
|
*)
|
|
638
|
-
echo
|
|
639
|
-
for
|
|
640
|
-
|
|
641
|
-
if brew install "$dep" 2>/dev/null; then
|
|
642
|
-
echo " ✓ $dep installed"
|
|
643
|
-
else
|
|
644
|
-
echo " ✗ Failed to install $dep"
|
|
645
|
-
fi
|
|
646
|
-
done
|
|
333
|
+
echo
|
|
334
|
+
print_info "Shell '$shell_name' is not supported for integration. Use 'gitbash <command>' or aliases."
|
|
335
|
+
return 0
|
|
647
336
|
;;
|
|
648
337
|
esac
|
|
649
|
-
}
|
|
650
338
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
local shell_files=("$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile")
|
|
655
|
-
|
|
656
|
-
# Find the first existing shell rc file
|
|
657
|
-
for file in "${shell_files[@]}"; do
|
|
658
|
-
if [[ -f "$file" ]]; then
|
|
659
|
-
shell_rc="$file"
|
|
660
|
-
break
|
|
661
|
-
fi
|
|
662
|
-
done
|
|
663
|
-
|
|
664
|
-
if [[ -z "$shell_rc" ]]; then
|
|
665
|
-
echo
|
|
666
|
-
echo "No shell config file found (.zshrc, .bashrc, .bash_profile)"
|
|
339
|
+
echo
|
|
340
|
+
if [[ -f "$shell_rc" ]] && grep -q 'gitbash --init' "$shell_rc"; then
|
|
341
|
+
print_info "Shell integration already configured in $shell_rc"
|
|
667
342
|
return 0
|
|
668
343
|
fi
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
if grep -q 'alias.*gitbash' "$shell_rc" 2>/dev/null; then
|
|
679
|
-
has_alias=true
|
|
680
|
-
fi
|
|
681
|
-
|
|
682
|
-
# If neither init nor alias found, offer to add
|
|
683
|
-
if [[ "$has_init" == false && "$has_alias" == false ]]; then
|
|
684
|
-
echo
|
|
685
|
-
echo "Shell integration not found in $shell_rc"
|
|
686
|
-
local add_init
|
|
687
|
-
read -re -p "Add 'eval \"\$(gitbash --init)\"' to $shell_rc? (Y/n): " add_init
|
|
688
|
-
case "$add_init" in
|
|
689
|
-
[nN][oO]|[nN])
|
|
690
|
-
echo "Skipped shell integration."
|
|
691
|
-
;;
|
|
692
|
-
*)
|
|
693
|
-
echo >> "$shell_rc"
|
|
694
|
-
echo '# gitbash shell integration' >> "$shell_rc"
|
|
695
|
-
echo 'eval "$(gitbash --init)"' >> "$shell_rc"
|
|
696
|
-
echo
|
|
697
|
-
echo "Added to $shell_rc"
|
|
698
|
-
echo "Run 'source $shell_rc' or restart your shell to activate."
|
|
699
|
-
;;
|
|
700
|
-
esac
|
|
701
|
-
else
|
|
702
|
-
echo
|
|
703
|
-
echo "Shell integration already configured in $shell_rc"
|
|
344
|
+
if gb_confirm "Add 'eval \"\$(gitbash --init)\"' to $shell_rc?" y; then
|
|
345
|
+
{
|
|
346
|
+
echo
|
|
347
|
+
echo '# gitbash shell integration'
|
|
348
|
+
# shellcheck disable=SC2016 # written literally to the rc file
|
|
349
|
+
echo 'eval "$(gitbash --init)"'
|
|
350
|
+
} >> "$shell_rc"
|
|
351
|
+
print_success "Added to $shell_rc. Restart your shell or run 'source $shell_rc'."
|
|
704
352
|
fi
|
|
705
353
|
}
|
|
706
354
|
|
|
707
|
-
#
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
711
|
-
SOURCE="$(readlink "$SOURCE")"
|
|
712
|
-
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
|
|
713
|
-
done
|
|
714
|
-
SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
|
|
715
|
-
|
|
716
|
-
# Export so sourced command files can use it
|
|
717
|
-
export SCRIPT_DIR
|
|
718
|
-
|
|
719
|
-
COMMANDS_DIR="$SCRIPT_DIR/commands"
|
|
720
|
-
VERSION="$(grep -o '"version": *"[^"]*"' "$SCRIPT_DIR/package.json" | head -1 | cut -d'"' -f4)"
|
|
721
|
-
|
|
722
|
-
# Available commands
|
|
723
|
-
COMMANDS=(
|
|
724
|
-
"branch"
|
|
725
|
-
"cleanstash"
|
|
726
|
-
"cleanup"
|
|
727
|
-
"commit"
|
|
728
|
-
"commits"
|
|
729
|
-
"create"
|
|
730
|
-
"pr"
|
|
731
|
-
"stale"
|
|
732
|
-
"stash"
|
|
733
|
-
"stashes"
|
|
734
|
-
"status"
|
|
735
|
-
"switch"
|
|
736
|
-
"unstash"
|
|
737
|
-
"update"
|
|
738
|
-
)
|
|
355
|
+
# =============================================================================
|
|
356
|
+
# Help and shell integration
|
|
357
|
+
# =============================================================================
|
|
739
358
|
|
|
740
359
|
show_help() {
|
|
741
|
-
cat <<
|
|
742
|
-
gitbash - Interactive git utilities for bash
|
|
360
|
+
cat << EOF
|
|
361
|
+
gitbash $VERSION - Interactive git utilities for bash
|
|
743
362
|
|
|
744
363
|
Usage:
|
|
745
364
|
gitbash <command> [args...]
|
|
746
|
-
gitbash --help
|
|
747
|
-
gitbash --init
|
|
748
|
-
gitbash --config
|
|
749
|
-
gitbash --config-local
|
|
750
|
-
gitbash --config-user
|
|
365
|
+
gitbash --help | --version
|
|
366
|
+
gitbash --init [--prefix=PREFIX]
|
|
367
|
+
gitbash --config | --config-local | --config-user
|
|
751
368
|
|
|
752
369
|
Commands:
|
|
753
370
|
branch Interactive menu for branch operations
|
|
@@ -768,10 +385,11 @@ Commands:
|
|
|
768
385
|
Options:
|
|
769
386
|
-h, --help Show this help message
|
|
770
387
|
-v, --version Show version number
|
|
771
|
-
--init Print shell
|
|
388
|
+
--init Print shell functions for calling commands directly
|
|
389
|
+
(--prefix=gb- creates gb-commit, gb-switch, ...)
|
|
772
390
|
--config Configure gitbash and save to ~/.gitbashrc
|
|
773
|
-
--config-local Configure
|
|
774
|
-
--config-user Configure
|
|
391
|
+
--config-local Configure overrides for this repository (.gitbashrc, committed)
|
|
392
|
+
--config-user Configure personal overrides for this repository (.gitbashrc-user)
|
|
775
393
|
|
|
776
394
|
Examples:
|
|
777
395
|
gitbash commit fix bug -p # Commit and push
|
|
@@ -779,83 +397,87 @@ Examples:
|
|
|
779
397
|
gitbash create PROJ-123 fix # Create feature/PROJ-123-fix
|
|
780
398
|
|
|
781
399
|
Shell Integration:
|
|
782
|
-
Add to your .
|
|
783
|
-
eval "
|
|
400
|
+
Add to your .zshrc or .bashrc:
|
|
401
|
+
eval "\$(gitbash --init)"
|
|
784
402
|
|
|
785
403
|
This enables calling commands directly:
|
|
786
404
|
commit fix bug -p
|
|
787
405
|
switch feature
|
|
788
406
|
|
|
789
|
-
Dependencies:
|
|
790
|
-
brew install fzf
|
|
791
|
-
brew install git-delta
|
|
792
|
-
brew install bat
|
|
407
|
+
Dependencies (optional):
|
|
408
|
+
fzf >= $GB_FZF_MIN_VERSION Interactive menus (brew install fzf)
|
|
409
|
+
git-delta Better diffs (brew install git-delta)
|
|
410
|
+
bat Syntax highlighting (brew install bat)
|
|
793
411
|
EOF
|
|
794
412
|
}
|
|
795
413
|
|
|
414
|
+
# Print wrapper functions for the interactive shell. Commands still run in
|
|
415
|
+
# their own bash process, so nothing leaks into the user's shell.
|
|
796
416
|
show_init() {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
417
|
+
local prefix="" arg cmd bin_quoted
|
|
418
|
+
for arg in "$@"; do
|
|
419
|
+
case "$arg" in
|
|
420
|
+
--prefix=*) prefix="${arg#--prefix=}" ;;
|
|
421
|
+
*) print_error "Unknown option for --init: $arg"; return 1 ;;
|
|
422
|
+
esac
|
|
423
|
+
done
|
|
424
|
+
if ! [[ "$prefix" =~ ^[A-Za-z0-9_-]*$ ]]; then
|
|
425
|
+
print_error "Invalid prefix '$prefix' (use letters, digits, '-' or '_')."
|
|
426
|
+
return 1
|
|
427
|
+
fi
|
|
428
|
+
printf -v bin_quoted '%q' "$GITBASH_BIN"
|
|
429
|
+
echo "# gitbash shell integration - add to your .zshrc or .bashrc:"
|
|
430
|
+
echo "# eval \"\$(gitbash --init${prefix:+ --prefix=$prefix})\""
|
|
806
431
|
for cmd in "${COMMANDS[@]}"; do
|
|
807
|
-
|
|
808
|
-
echo "source \"\$GITBASH_COMMANDS_DIR/$cmd.sh\""
|
|
809
|
-
fi
|
|
432
|
+
echo "${prefix}${cmd}() { $bin_quoted $cmd \"\$@\"; }"
|
|
810
433
|
done
|
|
811
434
|
}
|
|
812
435
|
|
|
436
|
+
# =============================================================================
|
|
813
437
|
# Main entry point
|
|
438
|
+
# =============================================================================
|
|
439
|
+
|
|
814
440
|
main() {
|
|
815
|
-
load_config
|
|
816
441
|
case "${1:-}" in
|
|
817
442
|
-h|--help|"")
|
|
818
443
|
show_help
|
|
819
|
-
exit 0
|
|
820
444
|
;;
|
|
821
445
|
-v|--version)
|
|
822
446
|
echo "gitbash $VERSION"
|
|
823
|
-
exit 0
|
|
824
447
|
;;
|
|
825
448
|
--init)
|
|
826
|
-
|
|
827
|
-
|
|
449
|
+
shift
|
|
450
|
+
show_init "$@"
|
|
828
451
|
;;
|
|
829
452
|
--config)
|
|
830
|
-
|
|
831
|
-
exit 0
|
|
453
|
+
configure_wizard global
|
|
832
454
|
;;
|
|
833
455
|
--config-local)
|
|
834
|
-
|
|
835
|
-
exit 0
|
|
456
|
+
configure_wizard local
|
|
836
457
|
;;
|
|
837
458
|
--config-user)
|
|
838
|
-
|
|
839
|
-
|
|
459
|
+
configure_wizard user
|
|
460
|
+
;;
|
|
461
|
+
-*)
|
|
462
|
+
print_error "Unknown option '$1'"
|
|
463
|
+
echo "Run 'gitbash --help' for available options." >&2
|
|
464
|
+
return 1
|
|
840
465
|
;;
|
|
841
466
|
*)
|
|
842
467
|
local cmd="$1"
|
|
843
468
|
shift
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if [[ -f "$COMMANDS_DIR/$cmd.sh" ]]; then
|
|
847
|
-
# Source the command file and execute the function
|
|
848
|
-
source "$COMMANDS_DIR/$cmd.sh"
|
|
849
|
-
"$cmd" "$@"
|
|
850
|
-
else
|
|
851
|
-
echo "Error: Unknown command '$cmd'" >&2
|
|
469
|
+
if ! is_command "$cmd"; then
|
|
470
|
+
print_error "Unknown command '$cmd'"
|
|
852
471
|
echo "Run 'gitbash --help' for available commands." >&2
|
|
853
|
-
|
|
472
|
+
return 1
|
|
854
473
|
fi
|
|
474
|
+
gb_load_config
|
|
475
|
+
# shellcheck source=/dev/null
|
|
476
|
+
source "$COMMANDS_DIR/$cmd.sh"
|
|
477
|
+
"$cmd" "$@"
|
|
855
478
|
;;
|
|
856
479
|
esac
|
|
857
480
|
}
|
|
858
481
|
|
|
859
482
|
main "$@"
|
|
860
|
-
|
|
861
|
-
|
|
483
|
+
exit $?
|