#!/usr/bin/env bash # git_clean_branches # ------------------ # Elimina ramas locales ya mergeadas en la rama base (main/master por defecto). # Opcionalmente también elimina las ramas en remotes. # # USO: # source git_clean_branches.sh # git_clean_branches [base_branch] [--remote] [--force] # # ARGUMENTOS: # base_branch Rama base (opcional; autodetecta main/master si se omite) # --remote Elimina también las ramas en todos los remotes # --force Omite confirmación interactiva git_clean_branches() { local base_branch="" local delete_remote=false local force=false # Parsear argumentos for arg in "$@"; do case "$arg" in --remote) delete_remote=true ;; --force) force=true ;; *) [[ -z "$base_branch" ]] && base_branch="$arg" ;; esac done # Validar repo if ! git rev-parse --is-inside-work-tree &>/dev/null; then echo "git_clean_branches: el directorio actual no es un repositorio Git" >&2 return 1 fi # Detectar rama base if [[ -z "$base_branch" ]]; then if git show-ref --verify --quiet refs/heads/main; then base_branch="main" elif git show-ref --verify --quiet refs/heads/master; then base_branch="master" else echo "git_clean_branches: no se detectó main/master; pasa la rama base como argumento" >&2 return 1 fi fi echo "git_clean_branches: rama base = ${base_branch}" # Hacer prune de referencias remotas obsoletas echo "Haciendo prune de referencias remotas..." git remote | while IFS= read -r remote; do git remote prune "$remote" 2>/dev/null && echo " Pruned: ${remote}" done echo "" # Buscar ramas locales mergeadas local candidates candidates="$(git branch --merged "$base_branch" \ | grep -v -E "^\*|^\s*(main|master|develop|dev|staging|release)$" \ | sed 's/^[[:space:]]*//' \ | grep -v "^$" || true)" if [[ -z "$candidates" ]]; then echo "No hay ramas locales mergeadas para eliminar." return 0 fi echo "Ramas candidatas a eliminar:" echo "$candidates" | while IFS= read -r b; do echo " - ${b}" done echo "" # Confirmación (omitir si --force) if [[ "$force" != true ]]; then echo -n "¿Eliminar estas ramas locales? [y/N] " read -r answer [[ "$answer" != "y" && "$answer" != "Y" ]] && { echo "Operación cancelada."; return 0; } fi # Eliminar ramas locales local deleted=0 local failed=0 while IFS= read -r branch; do if git branch -d "$branch"; then echo " Eliminada local: ${branch}" deleted=$((deleted + 1)) else echo " No se pudo eliminar: ${branch}" >&2 failed=$((failed + 1)) fi done <<< "$candidates" echo "" echo "Resumen: ${deleted} eliminada(s), ${failed} fallida(s)" # Eliminar en remotes si se solicitó if [[ "$delete_remote" == true ]]; then local remotes remotes="$(git remote 2>/dev/null || true)" if [[ -n "$remotes" ]]; then echo "" echo "Eliminando ramas en remotes..." while IFS= read -r branch; do while IFS= read -r remote; do if git ls-remote --heads "$remote" "$branch" 2>/dev/null | grep -q "$branch"; then if git push "$remote" --delete "$branch"; then echo " Eliminada remota: ${remote}/${branch}" else echo " No se pudo eliminar remota: ${remote}/${branch}" >&2 fi fi done <<< "$remotes" done <<< "$candidates" fi fi echo "" echo "Limpieza completada." } # Ejecutar si se invoca directamente if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then git_clean_branches "$@" fi