From bcc86814b546c8bc9a24b0049a0a72881884390c Mon Sep 17 00:00:00 2001 From: fatalerrors Date: Wed, 3 Jun 2026 15:47:29 +0200 Subject: [PATCH] added gprune --deep parameter to allow deletion of forge merge --- profile.d/git.sh | 57 +++++++++++++++++++++++++++++++++++++++++------ profile.d/help.sh | 2 +- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/profile.d/git.sh b/profile.d/git.sh index c2685d9..a7c7031 100644 --- a/profile.d/git.sh +++ b/profile.d/git.sh @@ -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 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 # ------------------------------------------------------------------------------ diff --git a/profile.d/help.sh b/profile.d/help.sh index f4c8624..131baa7 100644 --- a/profile.d/help.sh +++ b/profile.d/help.sh @@ -70,7 +70,7 @@ help() printf "gacp\t\tAdd, commit and push changes (auto-pull if needed)\n" printf "genpwd\t\tGenerate one or more random secure passwords with configurable constraints\n" printf "ggraph\t\tDisplay decorated git history graph\n" - printf "gprune\t\tDelete local branches already merged into main branch\n" + printf "gprune\t\tDelete local branches already merged, or after remote deletion (MR / PR)\n" printf "greset\t\tReset branch to upstream (stash local, drop local commits)\n" printf "groot\t\tDisplay repository root path (or cd to it with -g)\n" printf "gsync\t\tFetch and rebase current branch onto upstream\n"