#!/bin/bash # iterate through all subfolders in the current directory (or $1 if provided), # and check: # is it a git rep? # if so is it clean (nothing to commit)? # # for each git repo print the name of the repo, the datestamp of the last commit, # the nubmer of changes since then, and whether it's clean or not. # if no argument is provided, use the current directory DIR="${1:-.}" # make directory canonical DIR=$(cd "$DIR" 2>/dev/null && pwd) || { echo "Error: Directory '$1' not found"; exit 1; } # Color codes RED='\033[0;31m' NC='\033[0m' # No Color # Temporary file to store repository data TEMP_FILE=$(mktemp) # shellcheck disable=SC2064 trap "rm -f $TEMP_FILE" EXIT # iterate through all subfolders in DIR for folder in "$DIR"/*; do # Skip if not a directory [ ! -d "$folder" ] && continue # Check if it's a git repository if [ -d "$folder/.git" ]; then # Get the folder name repo_name=$(basename "$folder") # Change to the repository directory cd "$folder" || continue # Get the last commit timestamp in seconds since epoch last_commit_timestamp=$(git log -1 --format="%ct" 2>/dev/null) if [ -n "$last_commit_timestamp" ]; then # Get current timestamp current_timestamp=$(date +%s) # Calculate difference in seconds diff_seconds=$((current_timestamp - last_commit_timestamp)) # Convert to hours with decimal precision # Using bc for floating point arithmetic hours_ago=$(echo "scale=1; $diff_seconds / 3600" | bc) # Ensure leading zero for values less than 1 if [[ $hours_ago =~ ^\. ]]; then hours_ago="0$hours_ago" fi # Format the hours number and "hours" label separately hours_num="$hours_ago" hours_label="hours" # Combine with right-justified formatting hours_formatted=$(printf "%8s %-6s" "$hours_num" "$hours_label") else hours_num="999999" # Large number for sorting hours_label="commits" hours_formatted=$(printf "%8s %-7s" "No" "$hours_label") fi # Count uncommitted changes (staged and unstaged) # Count modified, deleted, and untracked files staged_changes=$(git diff --cached --numstat 2>/dev/null | wc -l) unstaged_changes=$(git diff --numstat 2>/dev/null | wc -l) untracked_files=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l) total_changes=$((staged_changes + unstaged_changes + untracked_files)) # Store repo data with status prefix (D for dirty, C for clean) if [ $total_changes -eq 0 ]; then echo "C|${hours_num}|${total_changes}|${repo_name}|${hours_formatted}" >> "$TEMP_FILE" else # For dirty repos, collect the list of changed files changed_files="" # Get staged files staged_files=$(git diff --cached --name-only 2>/dev/null) if [ -n "$staged_files" ]; then while IFS= read -r file; do changed_files="${changed_files} [staged] $file\n" done <<< "$staged_files" fi # Get unstaged modified files unstaged_files=$(git diff --name-only 2>/dev/null) if [ -n "$unstaged_files" ]; then while IFS= read -r file; do changed_files="${changed_files} [modified] $file\n" done <<< "$unstaged_files" fi # Get untracked files untracked=$(git ls-files --others --exclude-standard 2>/dev/null) if [ -n "$untracked" ]; then while IFS= read -r file; do changed_files="${changed_files} [untracked] $file\n" done <<< "$untracked" fi # Remove trailing newline changed_files=$(echo -e "$changed_files" | sed '$ d') # Store with a special delimiter (|||) to separate file list echo "D|${hours_num}|${total_changes}|${repo_name}|${hours_formatted}|||${changed_files}" >> "$TEMP_FILE" fi # Return to the original directory cd "$DIR" || exit 1 fi done # Print header printf "%-40s %15s %-10s %-10s\n" "Repository" "Last Commit" "Changes" "Status" printf "%-40s %15s %-10s %-10s\n" "----------" "(hours)" "-------" "------" # Print dirty repositories first (sorted by changes, descending) if grep -q "^D|" "$TEMP_FILE" 2>/dev/null; then printf "\n=== DIRTY REPOSITORIES ===\n\n" grep "^D|" "$TEMP_FILE" | sort -t'|' -k3,3nr | while IFS='|' read -r status hours changes repo hours_fmt separator file_list; do printf "%-40s %15s ${RED}%-10s${NC} ${RED}%-10s${NC}\n" "$repo" "$hours_fmt" "$changes" "Dirty" # Print the changed files if they exist if [ -n "$file_list" ]; then echo -e "$file_list" fi done fi # Print clean repositories (sorted by hours, ascending) if grep -q "^C|" "$TEMP_FILE" 2>/dev/null; then printf "\n=== CLEAN REPOSITORIES ===\n\n" grep "^C|" "$TEMP_FILE" | sort -t'|' -k2,2n | while IFS='|' read -r status hours changes repo hours_fmt; do printf "%-40s %15s %-10s %-10s\n" "$repo" "$hours_fmt" "$changes" "Clean" done fi