Skip to content

Instantly share code, notes, and snippets.

@mvitaly
Last active November 19, 2023 00:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mvitaly/4752f2d5c6a267dd22ff6508b9a3113e to your computer and use it in GitHub Desktop.
Save mvitaly/4752f2d5c6a267dd22ff6508b9a3113e to your computer and use it in GitHub Desktop.
#!/bin/bash
set -euo pipefail
# Define color codes
RED="\033[0;31m"
GREEN="\033[0;32m"
YELLOW="\033[0;33m"
RESET="\033[0m"
current_branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo '')
default_branch=$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/@@')
# Check the last modification time of FETCH_HEAD
current_time=$(date +%s) # current time in seconds since 1970-01-01 00:00:00 UTC
git_fetch_head_path=$(git rev-parse --show-toplevel)/.git/FETCH_HEAD
last_fetch_time=$(stat -c %Y "$git_fetch_head_path") # last modification time of FETCH_HEAD in seconds since 1970-01-01 00:00:00 UTC
# If more than 1 hour (3600 seconds) has passed since the last fetch, then fetch again
if (( current_time - last_fetch_time > 3600 )); then
git fetch origin
echo ""
fi
max_branch_name_length=$(git for-each-ref refs/heads/ "--format=%(refname:short)" | awk '{ if (length > max) max = length } END { print max }')
# based on https://stackoverflow.com/a/56026209
git for-each-ref refs/heads/ "--format=%(refname:short)" | while read branch; do
padded_branch=$(printf "%-${max_branch_name_length}s" "$branch")
prefix=" "
[ "$branch" == "$current_branch" ] && prefix="* "
if git merge-base --is-ancestor $branch $default_branch; then
echo -e "${prefix}[${GREEN}V${RESET}] ${YELLOW}${padded_branch}${RESET} is fully contained within $default_branch and can be safely deleted"
else
mergeBase=$(git merge-base $default_branch $branch)
commitTree=$(git rev-parse "$branch^{tree}")
cherryCommit=$(git commit-tree $commitTree -p $mergeBase -m _)
ahead_count=$(git rev-list --count $default_branch..$branch)
behind_count=$(git rev-list --count $branch..$default_branch)
# Construct the ahead/behind message
ahead_msg=""
[ $ahead_count -gt 0 ] && ahead_msg="ahead $ahead_count"
behind_msg=""
[ $behind_count -gt 0 ] && behind_msg="behind $behind_count"
join_char=", "
[[ -z "$ahead_msg" || -z "$behind_msg" ]] && join_char=""
if [[ $(git cherry $default_branch $cherryCommit) == "-"* ]]; then
echo -e "${prefix}[${GREEN}V${RESET}] ${YELLOW}${padded_branch}${RESET} is squash/rebase merged into $default_branch and can be safely deleted [$ahead_msg$join_char$behind_msg]"
else
echo -e "${prefix}[${RED}x${RESET}] ${YELLOW}${padded_branch}${RESET} is ${RED}NOT${RESET} merged into $default_branch [$ahead_msg$join_char$behind_msg]"
fi
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment