You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DevOps-Bash-tools/git/git_branch_delete_squash_me...

89 lines
3.3 KiB
Bash

#!/usr/bin/env bash
# vim:ts=4:sts=4:sw=4:et
#
# Author: Hari Sekhon
# Date: 2024-08-27 12:06:49 +0200 (Tue, 27 Aug 2024)
#
# https///github.com/HariSekhon/DevOps-Bash-tools
#
# License: see accompanying Hari Sekhon LICENSE file
#
# If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish
#
# https://www.linkedin.com/in/HariSekhon
#
set -euo pipefail
[ -n "${DEBUG:-}" ] && set -x
srcdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1090,SC1091
. "$srcdir/lib/git.sh"
# shellcheck disable=SC2034,SC2154
usage_description="
Carefully detects if a Git squash merged branch you want to delete has no changes vs the default trunk branch before deleting it
Doing a branch force delete without these checks risks losing all unpushed code changes on a branch
You need to run this on a clean trunk branch as it will check for any changed files (only untracked files are ignored)
If you don't think you can lose code by deleting the wrong branch just consider what happens when you repeatedly
work on area, branching each time but haven't been able to automatically prune old branches. You'll use similar names
and then may be confused about which is the old one to delete, run 'git branch -D' on the wrong one and congrats
you've just permanently lost all your unpushed work on that branch
"
# used by usage() in lib/utils.sh
# shellcheck disable=SC2034
usage_args="<branch>"
# TODO: consider allowing a second arg to explicitly specify the trunk branch
# - shouldn't be needed in most sane cases though
# - client who has dual trunk repo with prod deployments running off both develop and master
# is a historical legacy mistake
help_usage "$@"
min_args 1 "$@"
branch="$1"
default_trunk_branch="$(default_branch)"
timestamp "Checking out default trunk branch '$default_trunk_branch'"
# this actually wipes out any previous the merge state test
# - if script were to crash out, simply re-running would be enough
git checkout "$default_trunk_branch"
echo
timestamp "Pulling latest changes"
git pull
echo
timestamp "Checking '$branch' branch contents vs '$default_trunk_branch' using a pseudo merge for any diffs"
# this git command only outputs to stderr so we must capture to stdout to test, and then re tee back to stderr
# to give good user visibility of the state message
output="$(git merge --no-ff --no-commit "$branch" 2>&1 | tee /dev/stderr)"
echo
# check for any changes in the checkout but ignore untracked files
if [ -z "$(git status --porcelain | sed '/^??/d')" ]; then
timestamp "No content changes detected"
else
timestamp "Changes detected from branch '$branch' or dirty checkout - branch may not be fully merged"
timestamp "Aborting for safety"
exit 1
fi
# "Automatic merge went well" is not enough - hence we need the content change check above
# and use this as a mere state confirmation here if we don't get an "Already up to date" message
if [[ "$output" =~ Already\ up\ to\ date|Automatic\ merge\ went\ well ]]; then
timestamp "Branch is safe to delete"
timestamp "Deleting branch '$branch'"
git branch -D "$branch"
else
timestamp "WARNING: branch '$branch' was not detected as safely merged into trunk '$default_trunk_branch'"
echo
fi
git merge --abort