Skip to content

Instantly share code, notes, and snippets.

@reduardo7
Created August 25, 2025 12:47
Show Gist options
  • Save reduardo7/87929d2b08130a0bc07dc47895ed1449 to your computer and use it in GitHub Desktop.
Save reduardo7/87929d2b08130a0bc07dc47895ed1449 to your computer and use it in GitHub Desktop.
GIT Repository History scanner
#!/bin/bash
# Analyzes git commit history to find ticket-related commits
# Usage: ./repository-history.sh [-h COMMIT_HASH] [-t TICKET] [-a AUTHOR] [-d DAYS]
# - Without arguments: Shows ticket commits from current branch (last 30 days)
# - -h COMMIT_HASH: Shows ticket commits up to that hash in current branch (within specified days)
# - -t TICKET: Filters results to show only commits for specific ticket (e.g., OV-12345)
# - -a AUTHOR: Filters results by author name/email (case-insensitive partial match)
# - -d DAYS: Number of days to look back (default: 30)
# - All flags can be combined: -h HASH -t TICKET -a AUTHOR -d DAYS
# Note: COMMIT_HASH must exist in current branch
# Tickets follow the configured pattern (configurable in CONFIGURATION SECTION)
set -e
# =============================================================================
# CONFIGURATION SECTION
# =============================================================================
# Customize these variables for different projects or ticket systems
# Ticket pattern configuration
TICKET_PREFIX="AB" # Ticket prefix (e.g., AB, JIRA, PROJ)
TICKET_PATTERN="$TICKET_PREFIX-[0-9]+" # Full regex pattern for tickets
TICKET_EXAMPLE="$TICKET_PREFIX-12345" # Example ticket for help messages
# Default values
DEFAULT_DAYS=30 # Default number of days to look back
# Display configuration
DATE_FORMAT="%Y-%m-%d %H:%M:%S" # Format for commit dates
SEPARATOR_LINE="================================================================="
# =============================================================================
# Function to display ticket commits from git log output
display_ticket_commits() {
local ticket_filter="$1"
local author_filter="$2"
local filter_pipeline="grep -E '$TICKET_PATTERN'"
# Add ticket filter if specified
if [ -n "$ticket_filter" ]; then
filter_pipeline="$filter_pipeline | grep -i '$ticket_filter'"
fi
# Add author filter if specified
if [ -n "$author_filter" ]; then
filter_pipeline="$filter_pipeline | grep -i '$author_filter'"
fi
eval "$filter_pipeline" | \
while IFS='|' read -r hash author date message; do
# Extract ticket numbers from the commit message
tickets=$(echo "$message" | grep -oE "$TICKET_PATTERN" | sort -u | tr '\n' ', ' | sed 's/,$//')
# Clean commit message by removing ticket prefix if it starts the message
# Handles patterns like: "OV-1234 message", "OV-1234 - message", "OV-1234: message", etc.
clean_message="$message"
if [[ "$message" =~ ^$TICKET_PREFIX-[0-9]+[[:space:]]*[-:]*[[:space:]]* ]]; then
clean_message=$(echo "$message" | sed -E "s/^$TICKET_PREFIX-[0-9]+[[:space:]]*[-:]*[[:space:]]*//" | sed 's/^[[:space:]]*//')
fi
echo "Commit: $hash"
echo "Author: $author"
echo "Date: $date"
echo "Ticket(s): $tickets"
echo "Message: $clean_message"
echo "---"
done
}
# Function to show summary statistics
show_summary() {
local context_desc="$1"
local ticket_filter="$2"
local author_filter="$3"
shift 3
local git_log_args=("$@")
echo
echo "Analysis complete!"
echo "Summary:"
echo "========"
total_commits=$(git log "${git_log_args[@]}" --oneline | wc -l | tr -d ' ')
# Build filter pipeline for counting
local filter_pipeline="git log \"${git_log_args[@]}\" --pretty=format:\"%h|%an|%ad|%s\" --date=format:\"$DATE_FORMAT\" | grep -E '$TICKET_PATTERN'"
if [ -n "$ticket_filter" ]; then
filter_pipeline="$filter_pipeline | grep -i '$ticket_filter'"
fi
if [ -n "$author_filter" ]; then
filter_pipeline="$filter_pipeline | grep -i '$author_filter'"
fi
ticket_commits=$(eval "$filter_pipeline" | wc -l | tr -d ' ')
echo "Total commits $context_desc: $total_commits"
# Build description of filters
local filter_desc=""
if [ -n "$ticket_filter" ] && [ -n "$author_filter" ]; then
filter_desc="with ticket $ticket_filter and author matching '$author_filter'"
elif [ -n "$ticket_filter" ]; then
filter_desc="with ticket $ticket_filter"
elif [ -n "$author_filter" ]; then
filter_desc="by author matching '$author_filter'"
else
filter_desc="with ticket references"
fi
echo "Commits $filter_desc: $ticket_commits"
# List unique tickets found
echo
echo "Unique tickets found:"
eval "$filter_pipeline" | while IFS='|' read -r hash author date message; do
echo "$message" | grep -oE "$TICKET_PATTERN"
done | sort -u | while read ticket; do
echo " - $ticket"
done
}
# Function to analyze commits with given git log arguments
analyze_commits() {
local context_desc="$1"
local ticket_filter="$2"
local author_filter="$3"
shift 3
local git_log_args=("$@")
git log "${git_log_args[@]}" --pretty=format:"%h|%an|%ad|%s" --date=format:"$DATE_FORMAT" | display_ticket_commits "$ticket_filter" "$author_filter"
show_summary "$context_desc" "$ticket_filter" "$author_filter" "${git_log_args[@]}"
}
# Parse arguments using flags
COMMIT_HASH=""
TICKET_FILTER=""
AUTHOR_FILTER=""
DAYS="$DEFAULT_DAYS"
while [[ $# -gt 0 ]]; do
case $1 in
-h|--hash)
COMMIT_HASH="$2"
shift 2
;;
-t|--ticket)
TICKET_FILTER="$2"
shift 2
;;
-a|--author)
AUTHOR_FILTER="$2"
shift 2
;;
-d|--days)
DAYS="$2"
# Validate that DAYS is a positive integer
if ! [[ "$DAYS" =~ ^[0-9]+$ ]] || [ "$DAYS" -eq 0 ]; then
echo "Error: Days must be a positive integer"
exit 1
fi
shift 2
;;
--help)
echo "Usage: $0 [-h COMMIT_HASH] [-t TICKET] [-a AUTHOR] [-d DAYS]"
echo " -h, --hash COMMIT_HASH Analyze commits up to specified hash"
echo " -t, --ticket TICKET Filter by specific ticket (e.g., $TICKET_EXAMPLE)"
echo " -a, --author AUTHOR Filter by author name/email (case-insensitive)"
echo " -d, --days DAYS Number of days to look back (default: $DEFAULT_DAYS)"
echo " --help Show this help message"
echo ""
echo "Examples:"
echo " $0 # Show all tickets from current branch (last $DEFAULT_DAYS days)"
echo " $0 -d 7 # Show tickets from last 7 days"
echo " $0 -h 8b8e791b -d 14 # Show tickets up to commit within last 14 days"
echo " $0 -t $TICKET_EXAMPLE # Show only commits for ticket $TICKET_EXAMPLE"
echo " $0 -a john -d 60 # Show commits by 'john' from last 60 days"
echo " $0 -h 8b8e791b -t $TICKET_EXAMPLE -a john -d 7 # Combine all filters"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Handle commit hash validation and analysis
if [ -n "$COMMIT_HASH" ]; then
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
# Validate if the commit hash exists in current branch
if ! git merge-base --is-ancestor "$COMMIT_HASH" HEAD 2>/dev/null && [ "$COMMIT_HASH" != "$(git rev-parse HEAD 2>/dev/null)" ]; then
# Commit hash is not in current branch or doesn't exist - return empty
exit 0
fi
echo "Analyzing commits reachable from: $COMMIT_HASH (last $DAYS days only)"
if [ -n "$TICKET_FILTER" ]; then
echo "Filtering by ticket: $TICKET_FILTER"
fi
if [ -n "$AUTHOR_FILTER" ]; then
echo "Filtering by author: $AUTHOR_FILTER"
fi
echo "Time range: last $DAYS days"
echo "Looking for commits containing ticket references ($TICKET_PREFIX-XXXXX)"
echo "$SEPARATOR_LINE"
echo
analyze_commits "reachable from $COMMIT_HASH (last $DAYS days)" "$TICKET_FILTER" "$AUTHOR_FILTER" "$COMMIT_HASH" --since="$DAYS days ago"
else
# Default behavior - last N days from current branch
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo "Analyzing commits from branch: $CURRENT_BRANCH"
if [ -n "$TICKET_FILTER" ]; then
echo "Filtering by ticket: $TICKET_FILTER"
fi
if [ -n "$AUTHOR_FILTER" ]; then
echo "Filtering by author: $AUTHOR_FILTER"
fi
echo "Time range: last $DAYS days"
echo "Looking for commits from the last $DAYS days containing ticket references ($TICKET_PREFIX-XXXXX)"
echo "$SEPARATOR_LINE"
echo
analyze_commits "in last $DAYS days" "$TICKET_FILTER" "$AUTHOR_FILTER" --since="$DAYS days ago"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment