Skip to content

Instantly share code, notes, and snippets.

@balanza
Created March 6, 2026 22:21
Show Gist options
  • Select an option

  • Save balanza/135a5a66048ce755cfd26e74b0823347 to your computer and use it in GitHub Desktop.

Select an option

Save balanza/135a5a66048ce755cfd26e74b0823347 to your computer and use it in GitHub Desktop.
Calculate the list of contributor that will lose their contribution after files are deleted
#!/usr/bin/env bash
#
# lost-contributors.sh
#
# 1. Lists files deleted in the current branch compared to a target branch
# 2. Finds contributors who ever participated in those deleted files (full history)
# but have no lines remaining in any surviving file
#
# Usage:
# ./lost-contributors.sh [target_branch]
#
# Defaults:
# target_branch = main
#
# Example:
# git checkout feature/remove-legacy-code
# ./lost-contributors.sh main
set -euo pipefail
TARGET_BRANCH="${1:-main}"
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD")
echo "=============================================="
echo " Lost Contributors Detector"
echo "=============================================="
echo ""
echo " Target branch : $TARGET_BRANCH"
echo " Current branch : $CURRENT_BRANCH"
echo ""
# ── Step 1: Find files deleted in the current branch vs target ──
echo "[1/4] Finding deleted files..."
echo ""
deleted_files_list=$(mktemp)
git diff --name-only --diff-filter=D "$TARGET_BRANCH"..."$CURRENT_BRANCH" > "$deleted_files_list"
deleted_count=$(wc -l < "$deleted_files_list" | tr -d ' ')
if [ "$deleted_count" -eq 0 ]; then
echo " ✅ No files were deleted. Nothing to check."
rm -f "$deleted_files_list"
exit 0
fi
echo " $deleted_count file(s) deleted:"
while IFS= read -r f; do
echo " - $f"
done < "$deleted_files_list"
echo ""
# ── Step 2: Get ALL contributors who ever touched the deleted files (full history) ──
echo "[2/4] Collecting all historical contributors of deleted files..."
echo ""
deleted_contributors_file=$(mktemp)
while IFS= read -r file; do
git log --all --follow --format='<%ae>' -- "$file" 2>/dev/null
done < "$deleted_files_list" | sort -u > "$deleted_contributors_file"
deleted_contrib_count=$(wc -l < "$deleted_contributors_file" | tr -d ' ')
echo " Found $deleted_contrib_count unique contributor(s) who ever touched deleted files."
echo ""
if [ "$deleted_contrib_count" -eq 0 ]; then
echo " ✅ No contributors found in deleted files."
rm -f "$deleted_files_list" "$deleted_contributors_file"
exit 0
fi
# ── Step 3: For each contributor in deleted files, check if they have ──
# ── lines in any surviving file on the current branch ──
echo "[3/4] Checking if those contributors have lines in surviving files..."
echo " (this may take a while on large repos)"
echo ""
# Get the full list of files on the current branch (the surviving files)
surviving_files_list=$(mktemp)
git ls-tree -r --name-only "$CURRENT_BRANCH" > "$surviving_files_list"
# Blame all surviving files once and collect all contributor emails
surviving_contributors_file=$(mktemp)
while IFS= read -r file; do
git blame --porcelain "$CURRENT_BRANCH" -- "$file" 2>/dev/null \
| grep "^author-mail " \
| sed 's/^author-mail //'
done < "$surviving_files_list" | sort -u > "$surviving_contributors_file"
surviving_contrib_count=$(wc -l < "$surviving_contributors_file" | tr -d ' ')
echo " Found $surviving_contrib_count unique contributor(s) in surviving files."
echo ""
# ── Step 4: Find contributors only in deleted files (not in surviving) ──
echo "[4/4] Comparing..."
echo ""
lost_file=$(mktemp)
comm -23 "$deleted_contributors_file" "$surviving_contributors_file" > "$lost_file"
lost_count=$(wc -l < "$lost_file" | tr -d ' ')
echo "=============================================="
echo " Results"
echo "=============================================="
echo ""
if [ "$lost_count" -eq 0 ]; then
echo " ✅ No contributors will be fully removed."
echo " All contributors in deleted files also have lines elsewhere."
else
echo "## Lost contributors"
echo ""
echo "$lost_count contributor(s) participated in deleted files and have no lines remaining in the surviving codebase:"
echo ""
# Collect sorted usernames into an array
usernames=()
while IFS= read -r email; do
clean_email=$(echo "$email" | tr -d '<>')
if echo "$clean_email" | grep -q '@users\.noreply\.github\.com$'; then
gh_user=$(echo "$clean_email" | sed 's/@users\.noreply\.github\.com$//' | sed 's/^[0-9]*+//')
else
gh_user=$(echo "$clean_email" | cut -d'@' -f1)
fi
usernames+=("@${gh_user}")
done < "$lost_file"
# Sort alphabetically (case-insensitive)
IFS=$'\n' sorted=($(printf '%s\n' "${usernames[@]}" | sort -f)); unset IFS
# Output as a 4-column markdown table (no header)
echo "| | | | |"
echo "|---|---|---|---|"
col=0
row=""
for user in "${sorted[@]}"; do
row="${row}| ${user} "
col=$((col + 1))
if [ "$col" -eq 4 ]; then
echo "${row}|"
row=""
col=0
fi
done
# Handle remaining users in the last incomplete row
if [ "$col" -gt 0 ]; then
while [ "$col" -lt 4 ]; do
row="${row}| "
col=$((col + 1))
done
echo "${row}|"
fi
fi
echo ""
# Cleanup
rm -f "$deleted_files_list" "$deleted_contributors_file" \
"$surviving_files_list" "$surviving_contributors_file" "$lost_file"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment