git-jira-shortcuts 1.0.8 → 1.0.10
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/bin/cli.js +1 -1
- package/package.json +1 -1
- package/shell/git-extras.sh +180 -5
package/bin/cli.js
CHANGED
|
@@ -172,7 +172,7 @@ After init, these zsh commands are available:
|
|
|
172
172
|
gpu/gpush Push with tracking gdel/gdelete Delete branch
|
|
173
173
|
gl/glist List pending files gr/greset Reset files
|
|
174
174
|
gdiff Diff vs target branch grecent Recent branches
|
|
175
|
-
grepos Show all repo branches
|
|
175
|
+
grepos Show all repo branches gclean Delete merged branches
|
|
176
176
|
ghelp Show all commands
|
|
177
177
|
`);
|
|
178
178
|
}
|
package/package.json
CHANGED
package/shell/git-extras.sh
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
# GJS_JIRA_API_TOKEN — Base64 Jira API token
|
|
9
9
|
# GJS_BRANCH_WEBHOOK_URL — Optional webhook for branch name generation
|
|
10
10
|
# GJS_REPOS — Optional array of repo paths for grepos
|
|
11
|
+
# GJS_CLEAN_PROTECTED — Optional array of branch names to never delete with gclean
|
|
12
|
+
# GJS_BRANCH_ALIASES — Optional array of branch aliases (e.g. "m:master" "d:develop")
|
|
11
13
|
|
|
12
14
|
# Store path to this script for self-reference (used by ghelp)
|
|
13
15
|
GJS_SHELL_SCRIPT_PATH="${0:A}"
|
|
@@ -129,13 +131,32 @@ _gjs_resolve_branch_input() {
|
|
|
129
131
|
return 0
|
|
130
132
|
fi
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
# Check configurable aliases first, fall back to defaults
|
|
135
|
+
local resolved_alias=""
|
|
136
|
+
if [[ -n "${GJS_BRANCH_ALIASES+x}" ]]; then
|
|
137
|
+
for alias_entry in "${GJS_BRANCH_ALIASES[@]}"; do
|
|
138
|
+
local alias_key="${alias_entry%%:*}"
|
|
139
|
+
local alias_val="${alias_entry#*:}"
|
|
140
|
+
if [[ "$raw_input" == "$alias_key" ]]; then
|
|
141
|
+
resolved_alias="$alias_val"
|
|
142
|
+
break
|
|
143
|
+
fi
|
|
144
|
+
done
|
|
145
|
+
fi
|
|
146
|
+
if [[ -n "$resolved_alias" ]]; then
|
|
147
|
+
echo "$resolved_alias"
|
|
137
148
|
return 0
|
|
138
149
|
fi
|
|
150
|
+
# Default aliases if GJS_BRANCH_ALIASES not set
|
|
151
|
+
if [[ -z "${GJS_BRANCH_ALIASES+x}" ]]; then
|
|
152
|
+
if [[ "$raw_input" == "m" ]]; then
|
|
153
|
+
echo "master"
|
|
154
|
+
return 0
|
|
155
|
+
elif [[ "$raw_input" == "d" ]]; then
|
|
156
|
+
echo "develop"
|
|
157
|
+
return 0
|
|
158
|
+
fi
|
|
159
|
+
fi
|
|
139
160
|
|
|
140
161
|
if ! _gjs_is_ticket_number "$raw_input"; then
|
|
141
162
|
echo "$raw_input"
|
|
@@ -337,6 +358,11 @@ ghelp() { # ghelp | Show all git-jira-shortcuts commands
|
|
|
337
358
|
gr [file] Reset a file with confirmation — picker if no file
|
|
338
359
|
greset (same)
|
|
339
360
|
|
|
361
|
+
── Cleanup ────────────────────────────────────────────────────
|
|
362
|
+
gclean Delete local branches already merged to master
|
|
363
|
+
Skips: recent branches, master, develop, protected
|
|
364
|
+
Use --dry-run to preview without deleting
|
|
365
|
+
|
|
340
366
|
── Utilities ──────────────────────────────────────────────────
|
|
341
367
|
grepos / repos Show all repo clones and their current branch
|
|
342
368
|
testJira / tj Test your Jira API connection
|
|
@@ -364,6 +390,12 @@ ghelp() { # ghelp | Show all git-jira-shortcuts commands
|
|
|
364
390
|
|
|
365
391
|
Branch shorthand:
|
|
366
392
|
m → master d → develop
|
|
393
|
+
Customize in ~/.git-jira-shortcuts.env:
|
|
394
|
+
GJS_BRANCH_ALIASES=("m:main" "d:dev" "s:staging")
|
|
395
|
+
|
|
396
|
+
gclean config:
|
|
397
|
+
Protect additional branches:
|
|
398
|
+
GJS_CLEAN_PROTECTED=("release" "hotfix" "staging")
|
|
367
399
|
EOF
|
|
368
400
|
}
|
|
369
401
|
|
|
@@ -918,3 +950,146 @@ testJira() { # testJira | Test Jira API connection
|
|
|
918
950
|
return 0
|
|
919
951
|
}
|
|
920
952
|
alias tj='testJira' # testJira | Test Jira API connection
|
|
953
|
+
|
|
954
|
+
gclean() { # gclean [--dry-run] | Delete local branches already merged to master (skips recent)
|
|
955
|
+
local dry_run=0
|
|
956
|
+
|
|
957
|
+
for arg in "$@"; do
|
|
958
|
+
case "$arg" in
|
|
959
|
+
--dry-run|-n)
|
|
960
|
+
dry_run=1
|
|
961
|
+
;;
|
|
962
|
+
esac
|
|
963
|
+
done
|
|
964
|
+
|
|
965
|
+
if ! git rev-parse --git-dir >/dev/null 2>&1; then
|
|
966
|
+
echo "❌ Not a git repository."
|
|
967
|
+
return 1
|
|
968
|
+
fi
|
|
969
|
+
|
|
970
|
+
# Safety: refresh remote refs first so merge checks are based on current origin state.
|
|
971
|
+
if ! git fetch --quiet origin --prune 2>/dev/null; then
|
|
972
|
+
echo "❌ Could not fetch origin. Aborting to avoid deleting against stale refs."
|
|
973
|
+
return 1
|
|
974
|
+
fi
|
|
975
|
+
|
|
976
|
+
# Default protected branches
|
|
977
|
+
local -a protected=("master" "main" "develop" "development")
|
|
978
|
+
|
|
979
|
+
# Add user-configured protected branches
|
|
980
|
+
if [[ -n "${GJS_CLEAN_PROTECTED+x}" ]]; then
|
|
981
|
+
for branch in "${GJS_CLEAN_PROTECTED[@]}"; do
|
|
982
|
+
protected+=("$branch")
|
|
983
|
+
done
|
|
984
|
+
fi
|
|
985
|
+
|
|
986
|
+
# Also add alias targets to protected list (custom or default)
|
|
987
|
+
if [[ -n "${GJS_BRANCH_ALIASES+x}" ]]; then
|
|
988
|
+
for alias_entry in "${GJS_BRANCH_ALIASES[@]}"; do
|
|
989
|
+
local alias_val="${alias_entry#*:}"
|
|
990
|
+
protected+=("$alias_val")
|
|
991
|
+
done
|
|
992
|
+
else
|
|
993
|
+
# Default alias targets (m→master, d→develop) — same fallback as _gjs_resolve_branch_input
|
|
994
|
+
protected+=("master" "develop")
|
|
995
|
+
fi
|
|
996
|
+
|
|
997
|
+
# Get recent branches (worked on in last N days)
|
|
998
|
+
local -a recent_branches=()
|
|
999
|
+
while IFS= read -r entry; do
|
|
1000
|
+
[[ -z "$entry" ]] && continue
|
|
1001
|
+
recent_branches+=("$entry")
|
|
1002
|
+
done < <(git for-each-ref --sort=-committerdate --format='%(refname:short)' refs/heads/ | head -10)
|
|
1003
|
+
|
|
1004
|
+
# Also get branches from reflog (recently checked out)
|
|
1005
|
+
while IFS= read -r branch; do
|
|
1006
|
+
[[ -z "$branch" ]] && continue
|
|
1007
|
+
if [[ -z "${recent_branches[(r)$branch]}" ]]; then
|
|
1008
|
+
recent_branches+=("$branch")
|
|
1009
|
+
fi
|
|
1010
|
+
done < <(_gjs_get_recent_branches 2>/dev/null)
|
|
1011
|
+
|
|
1012
|
+
# Determine which branch to check merges against
|
|
1013
|
+
local merge_target=""
|
|
1014
|
+
for candidate in master main; do
|
|
1015
|
+
if git show-ref --verify --quiet "refs/remotes/origin/$candidate" 2>/dev/null; then
|
|
1016
|
+
merge_target="origin/$candidate"
|
|
1017
|
+
break
|
|
1018
|
+
fi
|
|
1019
|
+
done
|
|
1020
|
+
|
|
1021
|
+
if [[ -z "$merge_target" ]]; then
|
|
1022
|
+
echo "❌ Could not find master or main branch on origin."
|
|
1023
|
+
return 1
|
|
1024
|
+
fi
|
|
1025
|
+
|
|
1026
|
+
echo "🧹 Cleaning up branches merged into ${merge_target#origin/}..."
|
|
1027
|
+
[[ $dry_run -eq 1 ]] && echo " (dry-run mode — no branches will be deleted)"
|
|
1028
|
+
echo ""
|
|
1029
|
+
|
|
1030
|
+
local deleted=0
|
|
1031
|
+
local skipped_protected=0
|
|
1032
|
+
local skipped_recent=0
|
|
1033
|
+
local skipped_not_merged=0
|
|
1034
|
+
|
|
1035
|
+
while IFS= read -r branch; do
|
|
1036
|
+
branch=$(echo "$branch" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
1037
|
+
[[ -z "$branch" ]] && continue
|
|
1038
|
+
[[ "$branch" == \** ]] && continue
|
|
1039
|
+
|
|
1040
|
+
# Skip protected branches
|
|
1041
|
+
local is_protected=0
|
|
1042
|
+
for p in "${protected[@]}"; do
|
|
1043
|
+
if [[ "$branch" == "$p" ]]; then
|
|
1044
|
+
is_protected=1
|
|
1045
|
+
break
|
|
1046
|
+
fi
|
|
1047
|
+
done
|
|
1048
|
+
if [[ $is_protected -eq 1 ]]; then
|
|
1049
|
+
((skipped_protected++))
|
|
1050
|
+
continue
|
|
1051
|
+
fi
|
|
1052
|
+
|
|
1053
|
+
# Skip recently worked branches
|
|
1054
|
+
local is_recent=0
|
|
1055
|
+
for r in "${recent_branches[@]}"; do
|
|
1056
|
+
if [[ "$branch" == "$r" ]]; then
|
|
1057
|
+
is_recent=1
|
|
1058
|
+
break
|
|
1059
|
+
fi
|
|
1060
|
+
done
|
|
1061
|
+
if [[ $is_recent -eq 1 ]]; then
|
|
1062
|
+
((skipped_recent++))
|
|
1063
|
+
[[ $dry_run -eq 1 ]] && echo " ⏭️ Skip (recent): $branch"
|
|
1064
|
+
continue
|
|
1065
|
+
fi
|
|
1066
|
+
|
|
1067
|
+
# Extra safety: branch tip must still be an ancestor of merge target right before delete.
|
|
1068
|
+
if ! git merge-base --is-ancestor "$branch" "$merge_target" 2>/dev/null; then
|
|
1069
|
+
((skipped_not_merged++))
|
|
1070
|
+
[[ $dry_run -eq 1 ]] && echo " ⏭️ Skip (not merged): $branch"
|
|
1071
|
+
continue
|
|
1072
|
+
fi
|
|
1073
|
+
|
|
1074
|
+
# Delete the branch
|
|
1075
|
+
if [[ $dry_run -eq 1 ]]; then
|
|
1076
|
+
echo " 🗑️ Would delete: $branch"
|
|
1077
|
+
else
|
|
1078
|
+
if git branch -d "$branch" 2>/dev/null; then
|
|
1079
|
+
echo " 🗑️ Deleted: $branch"
|
|
1080
|
+
((deleted++))
|
|
1081
|
+
fi
|
|
1082
|
+
fi
|
|
1083
|
+
done < <(git branch --merged "$merge_target" 2>/dev/null)
|
|
1084
|
+
|
|
1085
|
+
echo ""
|
|
1086
|
+
if [[ $dry_run -eq 1 ]]; then
|
|
1087
|
+
echo "Dry run complete. Use 'gclean' without --dry-run to delete."
|
|
1088
|
+
else
|
|
1089
|
+
echo "✅ Deleted $deleted branch(es)."
|
|
1090
|
+
fi
|
|
1091
|
+
[[ $skipped_protected -gt 0 ]] && echo " Skipped $skipped_protected protected branch(es)."
|
|
1092
|
+
[[ $skipped_recent -gt 0 ]] && echo " Skipped $skipped_recent recent branch(es)."
|
|
1093
|
+
[[ $skipped_not_merged -gt 0 ]] && echo " Skipped $skipped_not_merged not-merged branch(es)."
|
|
1094
|
+
}
|
|
1095
|
+
alias gc_clean='gclean' # gclean | Alias for gclean
|