Skip to content

Instantly share code, notes, and snippets.

@Konamiman
Created November 7, 2025 12:00
Show Gist options
  • Save Konamiman/4a0073c540361aff7d48aadb41afab60 to your computer and use it in GitHub Desktop.
Save Konamiman/4a0073c540361aff7d48aadb41afab60 to your computer and use it in GitHub Desktop.
Count git changed and deleted lines in the working tree or in the staged files
#!/bin/bash
# chgcount.sh - Count lines added/modified and deleted in git diff
#
# Usage: chgcount.sh [OPTIONS]
#
# Options:
# -p, --pattern PATTERN File pattern to check (default: all files)
# Use **/ prefix for recursive matching in subdirectories
# -f, --files FILES Explicit space-separated list of files to check
# -s, --staged Look into staged files (default: unstaged)
# -b, --branch BRANCH Compare against branch (default: current working tree)
# -d, --details Show detailed per-file statistics
# -h, --help Show this help message
#
# Pattern Examples:
# "*.php" Matches PHP files in current directory only
# "**/*.php" Matches PHP files recursively in all subdirectories
# "**/Rest*.php" Matches Rest*.php files anywhere in the tree
# "src/**/*.php" Matches PHP files only under src/ directory
#
# Usage Examples:
# chgcount.sh # Count unstaged changes
# chgcount.sh -s # Count staged changes
# chgcount.sh -p "**/*.php" # Count all PHP files recursively
# chgcount.sh -p "**/Rest*.php" # Count Rest*.php files anywhere
# chgcount.sh -f "file1.php file2.php" # Count specific files only
# chgcount.sh -b trunk # Compare current branch against trunk
# chgcount.sh -b trunk -p "**/*.php" -d # PHP files vs trunk with details
set -e
# Default values
PATTERN=""
FILE_LIST=""
STAGED=false
BRANCH=""
DETAILS=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-p|--pattern)
PATTERN="$2"
shift 2
;;
-f|--files)
FILE_LIST="$2"
shift 2
;;
-s|--staged)
STAGED=true
shift
;;
-b|--branch)
BRANCH="$2"
shift 2
;;
-d|--details)
DETAILS=true
shift
;;
-h|--help)
sed -n '/^# chgcount.sh/,/^$/p' "$0" | sed 's/^# \?//' | sed '/^$/d'
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use -h or --help for usage information"
exit 1
;;
esac
done
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "Error: Not a git repository"
exit 1
fi
# Validate that pattern and files are not both specified
if [ -n "$PATTERN" ] && [ -n "$FILE_LIST" ]; then
echo "Error: Cannot use both --pattern and --files options together"
exit 1
fi
# Build git diff command
GIT_CMD="git diff --numstat"
if [ "$STAGED" = true ]; then
GIT_CMD="$GIT_CMD --staged"
fi
if [ -n "$BRANCH" ]; then
GIT_CMD="$GIT_CMD $BRANCH"
fi
if [ -n "$PATTERN" ]; then
GIT_CMD="$GIT_CMD -- $PATTERN"
elif [ -n "$FILE_LIST" ]; then
GIT_CMD="$GIT_CMD -- $FILE_LIST"
fi
# Get the diff stats
DIFF_OUTPUT=$($GIT_CMD)
if [ -z "$DIFF_OUTPUT" ]; then
if [ "$STAGED" = true ]; then
echo "No staged changes found."
else
echo "No changes found in working tree."
fi
echo "Run with -h for more options"
exit 0
fi
# Parse the output and sum up the changes
ADDED=0
DELETED=0
FILES=0
declare -a FILE_DETAILS
while IFS=$'\t' read -r add del file; do
# Skip binary files (marked with -)
if [ "$add" = "-" ] || [ "$del" = "-" ]; then
continue
fi
ADDED=$((ADDED + add))
DELETED=$((DELETED + del))
FILES=$((FILES + 1))
# Store details for later if needed
if [ "$DETAILS" = true ]; then
FILE_DETAILS+=("$add|$del|$file")
fi
done <<< "$DIFF_OUTPUT"
# Check if run without arguments
NO_ARGS=false
if [ "$STAGED" = false ] && [ -z "$BRANCH" ] && [ -z "$PATTERN" ] && [ -z "$FILE_LIST" ]; then
NO_ARGS=true
fi
# Print results
echo "========================"
echo "Git Diff Statistics"
echo "========================"
if [ "$STAGED" = true ]; then
echo "Mode: Staged changes"
else
echo "Mode: Unstaged changes"
fi
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ -n "$BRANCH" ]; then
if [ "$STAGED" = true ]; then
echo "Comparing: staged changes in $CURRENT_BRANCH vs $BRANCH"
else
echo "Comparing: working tree changes in $CURRENT_BRANCH vs $BRANCH"
fi
else
if [ "$STAGED" = true ]; then
echo "Comparing: staged changes vs $CURRENT_BRANCH"
else
echo "Comparing: working tree changes vs $CURRENT_BRANCH"
fi
fi
if [ -n "$PATTERN" ]; then
echo "Pattern: $PATTERN"
elif [ -n "$FILE_LIST" ]; then
echo "Files: $FILE_LIST"
fi
echo "------------------------"
echo "Files changed: $FILES"
echo "Lines added/modified: $ADDED"
echo "Lines deleted: $DELETED"
echo "Net change: $((ADDED - DELETED))"
echo "========================"
# Show detailed breakdown if requested
if [ "$DETAILS" = true ] && [ ${#FILE_DETAILS[@]} -gt 0 ]; then
echo ""
echo "Per-file breakdown:"
echo "------------------------"
printf "%-10s %-10s %-10s %s\n" "Added" "Deleted" "Net" "File"
printf "%-10s %-10s %-10s %s\n" "------" "-------" "---" "----"
for entry in "${FILE_DETAILS[@]}"; do
IFS='|' read -r add del file <<< "$entry"
net=$((add - del))
printf "%-10s %-10s %-10s %s\n" "$add" "$del" "$net" "$file"
done
echo "========================"
fi
if [ "$NO_ARGS" = true ]; then
echo ""
echo "Run with -h for more options"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment