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 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 testJira Test Jira API
175
+ grepos Show all repo branches gclean Delete merged branches
176
176
  ghelp Show all commands
177
177
  `);
178
178
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-jira-shortcuts",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Git + Jira workflow shortcuts for zsh — interactive branch switching, auto-prefixed commits, Jira integration, and more.",
5
5
  "author": "chipallen2",
6
6
  "license": "MIT",
@@ -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
- if [[ "$raw_input" == "m" ]]; then
133
- echo "master"
134
- return 0
135
- elif [[ "$raw_input" == "d" ]]; then
136
- echo "develop"
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