added gprune --deep parameter to allow deletion of forge merge

This commit is contained in:
fatalerrors
2026-06-03 15:47:29 +02:00
parent 462efef034
commit bcc86814b5
2 changed files with 51 additions and 8 deletions

View File

@@ -497,11 +497,20 @@ export -f gwip
# ------------------------------------------------------------------------------
# Delete merged local branches (except protected branches)
# Usage: gprune [main-branch]
# Usage: gprune [-D|--deep] [main-branch]
#
# Default mode: deletes branches already merged into <main-branch> locally
# (git branch --merged).
# Deep mode (-D/--deep): additionally deletes branches whose tracking remote
# has disappeared (remote pruned after a merge request / pull request merge).
# Those branches are detected via `git fetch --prune` + remote-tracking gone.
# This is the common case when the MR was merged upstream and the remote
# branch was deleted by the forge. Deletion uses `git branch -D` (force)
# because the local branch has no merged ancestor that git can verify locally.
gprune()
{
local PARSED
PARSED=$(getopt -o h --long help -n 'gprune' -- "$@")
local PARSED deep=0
PARSED=$(getopt -o hD --long help,deep -n 'gprune' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"gprune --help\" to display usage."
@@ -512,10 +521,20 @@ gprune()
while true; do
case "$1" in
-h|--help)
printf "gprune: Delete local branches already merged into main branch.\n"
printf "Usage: gprune [main-branch]\n"
printf "gprune: Delete local branches already merged into main branch.\n\n"
printf "Usage: gprune [-D|--deep] [main-branch]\n\n"
printf "Options:\n"
printf "\t-D, --deep\tAlso delete branches whose upstream was removed\n"
printf "\t\t\t(remote deleted after MR/PR merge). Uses 'git branch -D'.\n"
printf "\t-h, --help\tDisplay this help screen\n\n"
printf "Arguments:\n"
printf "\tmain-branch\tBase branch to check merges against (default: auto-detected)\n"
return 0
;;
-D|--deep)
deep=1
shift
;;
--)
shift
break
@@ -532,6 +551,7 @@ gprune()
local base="${1:-$(_git_default_branch "$GIT_DEFAULT_REMOTE")}" current deleted=0
current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
# ── Standard mode: branches locally merged into base ──────────────────────
disp I "Pruning branches merged into $base..."
while IFS= read -r b; do
@@ -540,12 +560,35 @@ gprune()
[[ $b == "$base" ]] && continue
[[ $b == "master" || $b == "main" || $b == "develop" || $b == "dev" ]] && continue
git branch -d "$b" >/dev/null 2>&1 && {
printf "Deleted: %s\n" "$b"
printf "Deleted (merged): %s\n" "$b"
((deleted++))
}
done < <(git branch --merged "$base" | sed -E 's/^\*?\s*//')
(( deleted == 0 )) && disp I "No merged branches to delete."
# ── Deep mode: branches whose remote tracking ref was deleted upstream ─────
if (( deep )); then
disp I "Deep mode: pruning remote-tracking refs, then checking for gone branches..."
git fetch --prune --quiet
while IFS= read -r b; do
[[ -z $b ]] && continue
[[ $b == "$current" ]] && continue
[[ $b == "$base" ]] && continue
[[ $b == "master" || $b == "main" || $b == "develop" || $b == "dev" ]] && continue
# Verify the upstream is truly gone (not just unset).
local upstream
upstream=$(git rev-parse --abbrev-ref "${b}@{upstream}" 2>/dev/null)
[[ -z "$upstream" ]] && continue # no tracking branch at all — skip
# If the remote ref still exists, skip (not deleted upstream).
git show-ref --verify --quiet "refs/remotes/$upstream" 2>/dev/null && continue
git branch -D "$b" >/dev/null 2>&1 && {
printf "Deleted (gone upstream): %s\n" "$b"
((deleted++))
}
done < <(git branch -vv | sed -E 's/^\*?\s*//' | awk '/: gone]/ {print $1}')
fi
(( deleted == 0 )) && disp I "No branches to delete."
}
export -f gprune
# ------------------------------------------------------------------------------