gitbash 0.2.3 → 0.2.4
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/commands/cleanup.sh +84 -42
- package/commands/stale.sh +5 -1
- package/package.json +1 -1
- package/commands/stale.zsh +0 -404
package/commands/cleanup.sh
CHANGED
|
@@ -70,7 +70,6 @@ EOF
|
|
|
70
70
|
;;
|
|
71
71
|
esac
|
|
72
72
|
done
|
|
73
|
-
|
|
74
73
|
# -----------------------------
|
|
75
74
|
# 1. Check prerequisites
|
|
76
75
|
# -----------------------------
|
|
@@ -110,7 +109,11 @@ EOF
|
|
|
110
109
|
if [[ "$json_mode" == false ]]; then
|
|
111
110
|
echo "Fetching and pruning remote..."
|
|
112
111
|
fi
|
|
113
|
-
git fetch --prune origin 2>/dev/null
|
|
112
|
+
if ! git fetch --prune origin 2>/dev/null; then
|
|
113
|
+
if [[ "$json_mode" == false ]]; then
|
|
114
|
+
echo "⚠ Fetch failed; continuing with local data." >&2
|
|
115
|
+
fi
|
|
116
|
+
fi
|
|
114
117
|
|
|
115
118
|
# -----------------------------
|
|
116
119
|
# 3. Detect base branch
|
|
@@ -144,9 +147,12 @@ EOF
|
|
|
144
147
|
remote_branches=$(git for-each-ref --format='%(refname:short)' refs/remotes/origin | sed 's|^origin/||' | grep -v '^HEAD$' || true)
|
|
145
148
|
|
|
146
149
|
# Arrays to hold branches by category (format: timestamp|branch|relative_date|author_email|author_name)
|
|
147
|
-
local merged_branches
|
|
148
|
-
local stale_branches
|
|
149
|
-
local recent_branches
|
|
150
|
+
local -a merged_branches
|
|
151
|
+
local -a stale_branches
|
|
152
|
+
local -a recent_branches
|
|
153
|
+
merged_branches=()
|
|
154
|
+
stale_branches=()
|
|
155
|
+
recent_branches=()
|
|
150
156
|
|
|
151
157
|
# Process each local branch
|
|
152
158
|
while IFS= read -r branch; do
|
|
@@ -186,15 +192,26 @@ EOF
|
|
|
186
192
|
fi
|
|
187
193
|
done < <(git for-each-ref --format='%(refname:short)' refs/heads)
|
|
188
194
|
|
|
189
|
-
# Sort each category by timestamp (most recent first)
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
# Sort each category by timestamp (most recent first), compatible with bash 3.x (no mapfile)
|
|
196
|
+
_sort_desc() {
|
|
197
|
+
local _var="$1"
|
|
198
|
+
shift
|
|
199
|
+
local _out=()
|
|
200
|
+
while IFS= read -r line; do
|
|
201
|
+
_out+=("$line")
|
|
202
|
+
done < <(printf '%s\n' "$@" | sort -t'|' -k1 -rn)
|
|
203
|
+
# shellcheck disable=SC2034 # assigned via eval to caller var
|
|
204
|
+
eval "$_var=(\"\${_out[@]}\")"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if [[ ${merged_branches+set} && ${#merged_branches[@]} -gt 0 ]]; then
|
|
208
|
+
_sort_desc merged_branches "${merged_branches[@]}"
|
|
192
209
|
fi
|
|
193
|
-
if [[ ${#stale_branches[@]} -gt 0 ]]; then
|
|
194
|
-
|
|
210
|
+
if [[ ${stale_branches+set} && ${#stale_branches[@]} -gt 0 ]]; then
|
|
211
|
+
_sort_desc stale_branches "${stale_branches[@]}"
|
|
195
212
|
fi
|
|
196
|
-
if [[ ${#recent_branches[@]} -gt 0 ]]; then
|
|
197
|
-
|
|
213
|
+
if [[ ${recent_branches+set} && ${#recent_branches[@]} -gt 0 ]]; then
|
|
214
|
+
_sort_desc recent_branches "${recent_branches[@]}"
|
|
198
215
|
fi
|
|
199
216
|
|
|
200
217
|
# -----------------------------
|
|
@@ -216,7 +233,18 @@ EOF
|
|
|
216
233
|
}
|
|
217
234
|
|
|
218
235
|
# Process all branches for JSON output
|
|
219
|
-
|
|
236
|
+
local all_entries=()
|
|
237
|
+
if [[ ${merged_branches+set} ]]; then
|
|
238
|
+
all_entries+=("${merged_branches[@]}")
|
|
239
|
+
fi
|
|
240
|
+
if [[ ${stale_branches+set} ]]; then
|
|
241
|
+
all_entries+=("${stale_branches[@]}")
|
|
242
|
+
fi
|
|
243
|
+
if [[ ${recent_branches+set} ]]; then
|
|
244
|
+
all_entries+=("${recent_branches[@]}")
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
for entry in "${all_entries[@]}"; do
|
|
220
248
|
[[ -z "$entry" ]] && continue
|
|
221
249
|
|
|
222
250
|
local ts name rel_date email author
|
|
@@ -255,7 +283,8 @@ EOF
|
|
|
255
283
|
local max_branch_len=60
|
|
256
284
|
|
|
257
285
|
# Add merged branches (pre-selected)
|
|
258
|
-
|
|
286
|
+
if [[ ${merged_branches+set} ]]; then
|
|
287
|
+
for entry in "${merged_branches[@]}"; do
|
|
259
288
|
local branch date display_branch line
|
|
260
289
|
branch=$(echo "$entry" | cut -d'|' -f2)
|
|
261
290
|
date=$(echo "$entry" | cut -d'|' -f3)
|
|
@@ -267,36 +296,41 @@ EOF
|
|
|
267
296
|
branch_list+="$line"$'\n'
|
|
268
297
|
preselect_list+="$line"$'\n'
|
|
269
298
|
((idx++))
|
|
270
|
-
|
|
299
|
+
done
|
|
300
|
+
fi
|
|
271
301
|
|
|
272
302
|
# Add stale branches (pre-selected)
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
303
|
+
if [[ ${stale_branches+set} ]]; then
|
|
304
|
+
for entry in "${stale_branches[@]}"; do
|
|
305
|
+
local branch date display_branch line
|
|
306
|
+
branch=$(echo "$entry" | cut -d'|' -f2)
|
|
307
|
+
date=$(echo "$entry" | cut -d'|' -f3)
|
|
308
|
+
display_branch="$branch"
|
|
309
|
+
if [[ ${#display_branch} -gt $max_branch_len ]]; then
|
|
310
|
+
display_branch="${display_branch:0:$((max_branch_len - 3))}..."
|
|
311
|
+
fi
|
|
312
|
+
line=$(printf "[STALE] %-${max_branch_len}s %s\t%s" "$display_branch" "$date" "$branch")
|
|
313
|
+
branch_list+="$line"$'\n'
|
|
314
|
+
preselect_list+="$line"$'\n'
|
|
315
|
+
((idx++))
|
|
316
|
+
done
|
|
317
|
+
fi
|
|
286
318
|
|
|
287
319
|
# Add recent branches (not pre-selected)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
320
|
+
if [[ ${recent_branches+set} ]]; then
|
|
321
|
+
for entry in "${recent_branches[@]}"; do
|
|
322
|
+
local branch date display_branch line
|
|
323
|
+
branch=$(echo "$entry" | cut -d'|' -f2)
|
|
324
|
+
date=$(echo "$entry" | cut -d'|' -f3)
|
|
325
|
+
display_branch="$branch"
|
|
326
|
+
if [[ ${#display_branch} -gt $max_branch_len ]]; then
|
|
327
|
+
display_branch="${display_branch:0:$((max_branch_len - 3))}..."
|
|
328
|
+
fi
|
|
329
|
+
line=$(printf "[RECENT] %-${max_branch_len}s %s\t%s" "$display_branch" "$date" "$branch")
|
|
330
|
+
branch_list+="$line"$'\n'
|
|
331
|
+
((idx++))
|
|
332
|
+
done
|
|
333
|
+
fi
|
|
300
334
|
|
|
301
335
|
if [[ -z "$branch_list" ]]; then
|
|
302
336
|
echo "No local branches to clean up."
|
|
@@ -316,8 +350,16 @@ EOF
|
|
|
316
350
|
# -----------------------------
|
|
317
351
|
# 7. Run fzf picker
|
|
318
352
|
# -----------------------------
|
|
319
|
-
local
|
|
320
|
-
local
|
|
353
|
+
local merged_count=0
|
|
354
|
+
local stale_count=0
|
|
355
|
+
local recent_count=0
|
|
356
|
+
|
|
357
|
+
[[ ${merged_branches+set} ]] && merged_count=${#merged_branches[@]}
|
|
358
|
+
[[ ${stale_branches+set} ]] && stale_count=${#stale_branches[@]}
|
|
359
|
+
[[ ${recent_branches+set} ]] && recent_count=${#recent_branches[@]}
|
|
360
|
+
|
|
361
|
+
local total_count=$((merged_count + stale_count + recent_count))
|
|
362
|
+
local preselect_count=$((merged_count + stale_count))
|
|
321
363
|
|
|
322
364
|
# Build toggle sequence for pre-selection (toggle first N items)
|
|
323
365
|
local toggle_sequence="first"
|
package/commands/stale.sh
CHANGED
|
@@ -144,7 +144,11 @@ EOF
|
|
|
144
144
|
if [[ "$json_mode" == false ]]; then
|
|
145
145
|
echo "Fetching latest from remote..."
|
|
146
146
|
fi
|
|
147
|
-
git fetch --prune origin 2>/dev/null
|
|
147
|
+
if ! git fetch --prune origin 2>/dev/null; then
|
|
148
|
+
if [[ "$json_mode" == false ]]; then
|
|
149
|
+
echo "⚠ Fetch failed; continuing with local data." >&2
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
148
152
|
|
|
149
153
|
# -----------------------------
|
|
150
154
|
# 4. Build branch list sorted by date (oldest first)
|
package/package.json
CHANGED
package/commands/stale.zsh
DELETED
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
stale() {
|
|
2
|
-
# -----------------------------
|
|
3
|
-
# 0. Check for help flag and parse options
|
|
4
|
-
# -----------------------------
|
|
5
|
-
local json_mode=false
|
|
6
|
-
local my_mode=false
|
|
7
|
-
local filter_args=()
|
|
8
|
-
|
|
9
|
-
while [[ $# -gt 0 ]]; do
|
|
10
|
-
case "$1" in
|
|
11
|
-
-h|--help)
|
|
12
|
-
cat << 'EOF'
|
|
13
|
-
Usage: stale [OPTIONS] [FILTER...]
|
|
14
|
-
|
|
15
|
-
Show a list of remote branches ordered by last modification date (oldest first).
|
|
16
|
-
Useful for identifying stale branches that may need cleanup.
|
|
17
|
-
|
|
18
|
-
Arguments:
|
|
19
|
-
FILTER... Optional search filter words to pre-fill fzf (joined with spaces)
|
|
20
|
-
|
|
21
|
-
Options:
|
|
22
|
-
-h, --help Show this help message
|
|
23
|
-
-m, --my Pre-fill filter with your git username (from git config user.name)
|
|
24
|
-
--json Output branch data as JSON (non-interactive)
|
|
25
|
-
|
|
26
|
-
Features:
|
|
27
|
-
- Lists all remote branches sorted by last commit date (oldest first)
|
|
28
|
-
- By default, only shows branches older than 3 months (truly stale)
|
|
29
|
-
- Shows branch name, relative date, and author
|
|
30
|
-
- Interactive selection with fzf
|
|
31
|
-
- Preview shows recent commits on the selected branch
|
|
32
|
-
- Delete selected branches directly from the list
|
|
33
|
-
|
|
34
|
-
Navigation:
|
|
35
|
-
↑/↓ or j/k Navigate through branches
|
|
36
|
-
TAB Select/deselect branch (multi-select)
|
|
37
|
-
Ctrl-A Toggle showing all branches (including recent ones)
|
|
38
|
-
Enter Delete selected branch(es)
|
|
39
|
-
ESC/Ctrl-C Exit without action
|
|
40
|
-
|
|
41
|
-
Examples:
|
|
42
|
-
$ stale
|
|
43
|
-
Stale branches (oldest first) >
|
|
44
|
-
> feature/very-old 6 months ago Bob Wilson
|
|
45
|
-
feature/another-old-one 4 months ago Jane Smith
|
|
46
|
-
feature/old-feature 3 months ago John Doe
|
|
47
|
-
✖ Abort
|
|
48
|
-
|
|
49
|
-
# Press Ctrl-A to show all branches including recent ones
|
|
50
|
-
# Select branches with TAB, press Enter to delete
|
|
51
|
-
|
|
52
|
-
$ stale --my
|
|
53
|
-
# Pre-fills fzf filter with your git username to show only your branches
|
|
54
|
-
|
|
55
|
-
$ stale Product refactoring
|
|
56
|
-
# Pre-fills fzf filter with "Product refactoring"
|
|
57
|
-
|
|
58
|
-
$ stale --json
|
|
59
|
-
# Output JSON for scripting
|
|
60
|
-
|
|
61
|
-
Output format:
|
|
62
|
-
<branch-name> <relative-date> <author>
|
|
63
|
-
|
|
64
|
-
Requirements:
|
|
65
|
-
- Must be in a git repository
|
|
66
|
-
- fzf (fuzzy finder) - will prompt to install if not found (not required for --json)
|
|
67
|
-
|
|
68
|
-
EOF
|
|
69
|
-
return 0
|
|
70
|
-
;;
|
|
71
|
-
--json)
|
|
72
|
-
json_mode=true
|
|
73
|
-
shift
|
|
74
|
-
;;
|
|
75
|
-
-m|--my)
|
|
76
|
-
my_mode=true
|
|
77
|
-
shift
|
|
78
|
-
;;
|
|
79
|
-
*)
|
|
80
|
-
filter_args+=("$1")
|
|
81
|
-
shift
|
|
82
|
-
;;
|
|
83
|
-
esac
|
|
84
|
-
done
|
|
85
|
-
|
|
86
|
-
# -----------------------------
|
|
87
|
-
# 1. Check prerequisites
|
|
88
|
-
# -----------------------------
|
|
89
|
-
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
90
|
-
if [[ "$json_mode" == true ]]; then
|
|
91
|
-
echo "[]"
|
|
92
|
-
else
|
|
93
|
-
echo "Not inside a git repository."
|
|
94
|
-
fi
|
|
95
|
-
return 1
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
if [[ "$json_mode" == false ]]; then
|
|
99
|
-
if ! command -v fzf >/dev/null 2>&1; then
|
|
100
|
-
echo "fzf is not installed."
|
|
101
|
-
|
|
102
|
-
read -rp "Install fzf with Homebrew? (y/N): " ans
|
|
103
|
-
case "$ans" in
|
|
104
|
-
[yY][eE][sS]|[yY])
|
|
105
|
-
echo "Installing fzf with brew..."
|
|
106
|
-
if ! command -v brew >/dev/null 2>&1; then
|
|
107
|
-
echo "Homebrew is not installed. Aborting."
|
|
108
|
-
return 1
|
|
109
|
-
fi
|
|
110
|
-
brew install fzf || { echo "fzf install failed. Aborting."; return 1; }
|
|
111
|
-
if [[ -f "$(brew --prefix)/opt/fzf/install" ]]; then
|
|
112
|
-
echo "Running fzf install script..."
|
|
113
|
-
yes | "$(brew --prefix)/opt/fzf/install"
|
|
114
|
-
fi
|
|
115
|
-
;;
|
|
116
|
-
*)
|
|
117
|
-
echo "Aborted (skipped fzf installation)."
|
|
118
|
-
return 1
|
|
119
|
-
;;
|
|
120
|
-
esac
|
|
121
|
-
fi
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
# -----------------------------
|
|
125
|
-
# 2. Handle filter (--my flag or arbitrary words)
|
|
126
|
-
# -----------------------------
|
|
127
|
-
local filter=""
|
|
128
|
-
if [[ "$my_mode" == true ]]; then
|
|
129
|
-
filter=$(git config user.name)
|
|
130
|
-
if [[ -z "$filter" && "$json_mode" == false ]]; then
|
|
131
|
-
echo "Warning: git config user.name is not set."
|
|
132
|
-
fi
|
|
133
|
-
elif [[ ${#filter_args[@]} -gt 0 ]]; then
|
|
134
|
-
filter="${filter_args[*]}"
|
|
135
|
-
fi
|
|
136
|
-
|
|
137
|
-
# -----------------------------
|
|
138
|
-
# 3. Fetch latest from remote
|
|
139
|
-
# -----------------------------
|
|
140
|
-
if [[ "$json_mode" == false ]]; then
|
|
141
|
-
echo "Fetching latest from remote..."
|
|
142
|
-
fi
|
|
143
|
-
git fetch --prune origin 2>/dev/null
|
|
144
|
-
|
|
145
|
-
# -----------------------------
|
|
146
|
-
# 4. Build branch list sorted by date (oldest first)
|
|
147
|
-
# -----------------------------
|
|
148
|
-
local abort_label="✖ Abort"
|
|
149
|
-
local max_branch_length=65
|
|
150
|
-
|
|
151
|
-
# Calculate date 3 months ago
|
|
152
|
-
local three_months_ago
|
|
153
|
-
three_months_ago=$(date -v-3m +%s 2>/dev/null || date -d "3 months ago" +%s 2>/dev/null)
|
|
154
|
-
|
|
155
|
-
# Build full branch list (all branches)
|
|
156
|
-
# Format: display_branch | date | author | full_branch (tab-separated, last field is full branch for operations)
|
|
157
|
-
local all_branch_list
|
|
158
|
-
all_branch_list=$(git for-each-ref --sort=committerdate --format='%(refname:short)|%(committerdate:relative)|%(authorname)|%(committerdate:unix)' refs/remotes/origin | \
|
|
159
|
-
grep -v 'origin/HEAD' | \
|
|
160
|
-
grep -v '^origin$' | \
|
|
161
|
-
while IFS='|' read -r branch date author timestamp; do
|
|
162
|
-
# Remove origin/ prefix for display
|
|
163
|
-
local full_branch="${branch#origin/}"
|
|
164
|
-
local display_branch="$full_branch"
|
|
165
|
-
# Truncate branch name if too long
|
|
166
|
-
if [[ ${#display_branch} -gt $max_branch_length ]]; then
|
|
167
|
-
display_branch="${display_branch:0:$((max_branch_length - 3))}..."
|
|
168
|
-
fi
|
|
169
|
-
# Format: display branch (padded), date, author, TAB, full branch name
|
|
170
|
-
printf "%-${max_branch_length}s %-20s %-25s\t%s\n" "$display_branch" "$date" "$author" "$full_branch"
|
|
171
|
-
done || true)
|
|
172
|
-
|
|
173
|
-
# Build stale branch list (only branches older than 3 months)
|
|
174
|
-
local stale_branch_list
|
|
175
|
-
stale_branch_list=$(git for-each-ref --sort=committerdate --format='%(refname:short)|%(committerdate:relative)|%(authorname)|%(committerdate:unix)' refs/remotes/origin | \
|
|
176
|
-
grep -v 'origin/HEAD' | \
|
|
177
|
-
grep -v '^origin$' | \
|
|
178
|
-
while IFS='|' read -r branch date author timestamp; do
|
|
179
|
-
# Only include if older than 3 months
|
|
180
|
-
if [[ "$timestamp" -lt "$three_months_ago" ]]; then
|
|
181
|
-
# Remove origin/ prefix for display
|
|
182
|
-
local full_branch="${branch#origin/}"
|
|
183
|
-
local display_branch="$full_branch"
|
|
184
|
-
# Truncate branch name if too long
|
|
185
|
-
if [[ ${#display_branch} -gt $max_branch_length ]]; then
|
|
186
|
-
display_branch="${display_branch:0:$((max_branch_length - 3))}..."
|
|
187
|
-
fi
|
|
188
|
-
# Format: display branch (padded), date, author, TAB, full branch name
|
|
189
|
-
printf "%-${max_branch_length}s %-20s %-25s\t%s\n" "$display_branch" "$date" "$author" "$full_branch"
|
|
190
|
-
fi
|
|
191
|
-
done || true)
|
|
192
|
-
|
|
193
|
-
if [[ -z "$all_branch_list" ]]; then
|
|
194
|
-
if [[ "$json_mode" == true ]]; then
|
|
195
|
-
echo "[]"
|
|
196
|
-
else
|
|
197
|
-
echo "No remote branches found."
|
|
198
|
-
fi
|
|
199
|
-
return 0
|
|
200
|
-
fi
|
|
201
|
-
|
|
202
|
-
# -----------------------------
|
|
203
|
-
# 5. JSON mode output
|
|
204
|
-
# -----------------------------
|
|
205
|
-
if [[ "$json_mode" == true ]]; then
|
|
206
|
-
local json_output="["
|
|
207
|
-
local first=true
|
|
208
|
-
|
|
209
|
-
# Helper function to escape JSON strings
|
|
210
|
-
_json_escape() {
|
|
211
|
-
local str="$1"
|
|
212
|
-
str="${str//\\/\\\\}"
|
|
213
|
-
str="${str//\"/\\\"}"
|
|
214
|
-
str="${str//$'\n'/\\n}"
|
|
215
|
-
str="${str//$'\r'/\\r}"
|
|
216
|
-
str="${str//$'\t'/\\t}"
|
|
217
|
-
echo "$str"
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
# Build JSON from stale branches
|
|
221
|
-
{
|
|
222
|
-
while IFS='|' read -r branch rel_date author_name author_email timestamp; do
|
|
223
|
-
[[ -z "$branch" ]] && continue
|
|
224
|
-
# Only include if older than 3 months
|
|
225
|
-
if [[ "$timestamp" -lt "$three_months_ago" ]]; then
|
|
226
|
-
local name="${branch#origin/}"
|
|
227
|
-
# Remove angle brackets from email
|
|
228
|
-
author_email="${author_email#<}"
|
|
229
|
-
author_email="${author_email%>}"
|
|
230
|
-
|
|
231
|
-
# Escape values for JSON
|
|
232
|
-
name=$(_json_escape "$name")
|
|
233
|
-
rel_date=$(_json_escape "$rel_date")
|
|
234
|
-
author_email=$(_json_escape "$author_email")
|
|
235
|
-
author_name=$(_json_escape "$author_name")
|
|
236
|
-
|
|
237
|
-
if [[ "$first" == true ]]; then
|
|
238
|
-
first=false
|
|
239
|
-
else
|
|
240
|
-
json_output+=","
|
|
241
|
-
fi
|
|
242
|
-
|
|
243
|
-
json_output+="{\"last_change_timestamp\":$timestamp,\"author_email\":\"$author_email\",\"author_name\":\"$author_name\",\"name\":\"$name\",\"last_change_relative\":\"$rel_date\"}"
|
|
244
|
-
fi
|
|
245
|
-
done < <(git for-each-ref --sort=committerdate --format='%(refname:short)|%(committerdate:relative)|%(authorname)|%(authoremail)|%(committerdate:unix)' refs/remotes/origin 2>/dev/null | grep -v 'origin/HEAD' | grep -v '^origin$' || true)
|
|
246
|
-
} &>/dev/null
|
|
247
|
-
json_output+="]"
|
|
248
|
-
echo "$json_output"
|
|
249
|
-
return 0
|
|
250
|
-
fi
|
|
251
|
-
|
|
252
|
-
# Create temp files for fzf reload
|
|
253
|
-
local stale_file=$(mktemp)
|
|
254
|
-
local all_file=$(mktemp)
|
|
255
|
-
local state_file=$(mktemp)
|
|
256
|
-
echo "$stale_branch_list" > "$stale_file"
|
|
257
|
-
echo "$abort_label" >> "$stale_file"
|
|
258
|
-
echo "$all_branch_list" > "$all_file"
|
|
259
|
-
echo "$abort_label" >> "$all_file"
|
|
260
|
-
echo "stale" > "$state_file"
|
|
261
|
-
|
|
262
|
-
# -----------------------------
|
|
263
|
-
# 6. Run fzf picker with preview
|
|
264
|
-
# -----------------------------
|
|
265
|
-
local stale_count=$(echo "$stale_branch_list" | grep -c . || echo "0")
|
|
266
|
-
local all_count=$(echo "$all_branch_list" | grep -c . || echo "0")
|
|
267
|
-
|
|
268
|
-
# Create toggle script
|
|
269
|
-
local toggle_script=$(mktemp)
|
|
270
|
-
cat > "$toggle_script" << TOGGLE_EOF
|
|
271
|
-
#!/bin/bash
|
|
272
|
-
state=\$(cat "$state_file")
|
|
273
|
-
if [[ "\$state" == "stale" ]]; then
|
|
274
|
-
echo "all" > "$state_file"
|
|
275
|
-
cat "$all_file"
|
|
276
|
-
else
|
|
277
|
-
echo "stale" > "$state_file"
|
|
278
|
-
cat "$stale_file"
|
|
279
|
-
fi
|
|
280
|
-
TOGGLE_EOF
|
|
281
|
-
chmod +x "$toggle_script"
|
|
282
|
-
|
|
283
|
-
# Create prompt script
|
|
284
|
-
local prompt_script=$(mktemp)
|
|
285
|
-
cat > "$prompt_script" << PROMPT_EOF
|
|
286
|
-
#!/bin/bash
|
|
287
|
-
state=\$(cat "$state_file")
|
|
288
|
-
if [[ "\$state" == "stale" ]]; then
|
|
289
|
-
echo "Stale branches (>3 months, oldest first) > "
|
|
290
|
-
else
|
|
291
|
-
echo "All branches (oldest first) > "
|
|
292
|
-
fi
|
|
293
|
-
PROMPT_EOF
|
|
294
|
-
chmod +x "$prompt_script"
|
|
295
|
-
|
|
296
|
-
# Create header script
|
|
297
|
-
local header_script=$(mktemp)
|
|
298
|
-
cat > "$header_script" << HEADER_EOF
|
|
299
|
-
#!/bin/bash
|
|
300
|
-
state=\$(cat "$state_file")
|
|
301
|
-
if [[ "\$state" == "stale" ]]; then
|
|
302
|
-
echo "[TAB] select | [Ctrl-A] toggle all/stale | [Enter] delete | [ESC] exit
|
|
303
|
-
Showing $stale_count stale branches (older than 3 months)"
|
|
304
|
-
else
|
|
305
|
-
echo "[TAB] select | [Ctrl-A] toggle all/stale | [Enter] delete | [ESC] exit
|
|
306
|
-
Showing all $all_count branches"
|
|
307
|
-
fi
|
|
308
|
-
HEADER_EOF
|
|
309
|
-
chmod +x "$header_script"
|
|
310
|
-
|
|
311
|
-
local selection
|
|
312
|
-
selection=$(
|
|
313
|
-
fzf \
|
|
314
|
-
--prompt="Stale branches (>3 months, oldest first) > " \
|
|
315
|
-
--query="$filter" \
|
|
316
|
-
-i \
|
|
317
|
-
--reverse \
|
|
318
|
-
--border \
|
|
319
|
-
--header="[TAB] select | [Ctrl-A] toggle all/stale | [Enter] delete | [ESC] exit
|
|
320
|
-
Showing $stale_count stale branches (older than 3 months)" \
|
|
321
|
-
--multi \
|
|
322
|
-
--delimiter=$'\t' \
|
|
323
|
-
--with-nth=1 \
|
|
324
|
-
--bind=enter:accept \
|
|
325
|
-
--bind="ctrl-a:reload($toggle_script)+transform-prompt($prompt_script)+transform-header($header_script)" \
|
|
326
|
-
--preview='
|
|
327
|
-
line={}
|
|
328
|
-
if [[ "$line" == "✖ Abort" ]]; then
|
|
329
|
-
echo "Exit without action";
|
|
330
|
-
else
|
|
331
|
-
# Extract full branch name (after tab)
|
|
332
|
-
branch=$(echo "$line" | cut -f2)
|
|
333
|
-
echo "Branch: origin/$branch";
|
|
334
|
-
echo "";
|
|
335
|
-
echo "Recent commits:";
|
|
336
|
-
git log --oneline --color=always -n 15 "origin/$branch" 2>/dev/null || echo "No commits found";
|
|
337
|
-
fi
|
|
338
|
-
' \
|
|
339
|
-
--preview-window=right:35% \
|
|
340
|
-
< "$stale_file"
|
|
341
|
-
) </dev/tty || true
|
|
342
|
-
|
|
343
|
-
# Cleanup temp files
|
|
344
|
-
rm -f "$stale_file" "$all_file" "$state_file" "$toggle_script" "$prompt_script" "$header_script"
|
|
345
|
-
|
|
346
|
-
# ESC or Ctrl-C
|
|
347
|
-
if [[ -z "$selection" ]]; then
|
|
348
|
-
echo "Exited."
|
|
349
|
-
return 0
|
|
350
|
-
fi
|
|
351
|
-
|
|
352
|
-
# Abort option
|
|
353
|
-
if echo "$selection" | grep -q "^$abort_label"; then
|
|
354
|
-
echo "Aborted."
|
|
355
|
-
return 0
|
|
356
|
-
fi
|
|
357
|
-
|
|
358
|
-
# -----------------------------
|
|
359
|
-
# 7. Extract branch names and confirm deletion
|
|
360
|
-
# -----------------------------
|
|
361
|
-
local branches_to_delete=()
|
|
362
|
-
while IFS= read -r line; do
|
|
363
|
-
# Skip abort option
|
|
364
|
-
if [[ "$line" == "✖ Abort" ]]; then
|
|
365
|
-
continue
|
|
366
|
-
fi
|
|
367
|
-
# Extract full branch name (after tab)
|
|
368
|
-
local branch_name
|
|
369
|
-
branch_name=$(echo "$line" | cut -f2)
|
|
370
|
-
if [[ -n "$branch_name" ]]; then
|
|
371
|
-
branches_to_delete+=("$branch_name")
|
|
372
|
-
fi
|
|
373
|
-
done <<< "$selection"
|
|
374
|
-
|
|
375
|
-
if [[ ${#branches_to_delete[@]} -eq 0 ]]; then
|
|
376
|
-
echo "No branches selected."
|
|
377
|
-
return 0
|
|
378
|
-
fi
|
|
379
|
-
|
|
380
|
-
echo ""
|
|
381
|
-
echo "Selected branches to delete from remote:"
|
|
382
|
-
for branch in "${branches_to_delete[@]}"; do
|
|
383
|
-
echo " - origin/$branch"
|
|
384
|
-
done
|
|
385
|
-
echo ""
|
|
386
|
-
|
|
387
|
-
read -rp "Delete these ${#branches_to_delete[@]} branch(es) from remote? (y/N): " confirm
|
|
388
|
-
case "$confirm" in
|
|
389
|
-
[yY][eE][sS]|[yY])
|
|
390
|
-
echo "Deleting branches..."
|
|
391
|
-
for branch in "${branches_to_delete[@]}"; do
|
|
392
|
-
echo "Deleting origin/$branch ..."
|
|
393
|
-
if git push origin --delete "$branch" 2>/dev/null; then
|
|
394
|
-
echo "✓ Deleted origin/$branch"
|
|
395
|
-
else
|
|
396
|
-
echo "✗ Failed to delete origin/$branch"
|
|
397
|
-
fi
|
|
398
|
-
done
|
|
399
|
-
;;
|
|
400
|
-
*)
|
|
401
|
-
echo "Deletion cancelled."
|
|
402
|
-
;;
|
|
403
|
-
esac
|
|
404
|
-
}
|