|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Script to update author and commit dates for Git commits |
| 4 | +# Sets dates to N days ago with random times from two periods: 5:00 AM - 8:00 AM or 10:00 PM - midnight |
| 5 | +# Each commit randomly chooses one of these two time periods |
| 6 | +# |
| 7 | +# Usage: ./update_commit_dates.sh [number_of_commits] [days_ago] [time_period] |
| 8 | +# |
| 9 | +# Examples: |
| 10 | +# ./update_commit_dates.sh # Uses defaults (3 commits, 1 day ago, evening period) |
| 11 | +# ./update_commit_dates.sh 30 # Changes the last 30 commits to 1 day ago, evening period |
| 12 | +# ./update_commit_dates.sh 10 7 # Changes the last 10 commits to 7 days ago, evening period |
| 13 | +# ./update_commit_dates.sh 10 7 morning # Uses morning period (5h-8h) |
| 14 | +# ./update_commit_dates.sh 10 7 evening # Uses evening period (22h-midnight) |
| 15 | +# ./update_commit_dates.sh 10 7 both # Randomly chooses between both periods |
| 16 | + |
| 17 | +# Default values |
| 18 | +DEFAULT_NUM_COMMITS=3 |
| 19 | +DEFAULT_DAYS_AGO=1 |
| 20 | +DEFAULT_TIME_PERIOD="evening" |
| 21 | + |
| 22 | +# Check for unstaged changes |
| 23 | +if [[ -n $(git status --porcelain) ]]; then |
| 24 | + echo "Error: You have unstaged changes in your working directory." |
| 25 | + echo "Please commit or stash your changes before running this script." |
| 26 | + exit 1 |
| 27 | +fi |
| 28 | + |
| 29 | +# Parse command line arguments |
| 30 | +if [ $# -eq 0 ]; then |
| 31 | + NUM_COMMITS=$DEFAULT_NUM_COMMITS |
| 32 | + DAYS_AGO=$DEFAULT_DAYS_AGO |
| 33 | + TIME_PERIOD=$DEFAULT_TIME_PERIOD |
| 34 | + echo "No arguments provided. Using defaults: $NUM_COMMITS commits, $DAYS_AGO day(s) ago, $TIME_PERIOD period." |
| 35 | +elif [ $# -eq 1 ]; then |
| 36 | + # Validate that the argument is a positive integer |
| 37 | + if [[ $1 =~ ^[0-9]+$ ]] && [ $1 -gt 0 ]; then |
| 38 | + NUM_COMMITS=$1 |
| 39 | + DAYS_AGO=$DEFAULT_DAYS_AGO |
| 40 | + TIME_PERIOD=$DEFAULT_TIME_PERIOD |
| 41 | + echo "Will modify the last $NUM_COMMITS commits to $DAYS_AGO day(s) ago using $TIME_PERIOD period." |
| 42 | + else |
| 43 | + echo "Error: First argument must be a positive integer." |
| 44 | + echo "Usage: $0 [number_of_commits] [days_ago] [time_period]" |
| 45 | + exit 1 |
| 46 | + fi |
| 47 | +elif [ $# -eq 2 ]; then |
| 48 | + # Validate both arguments are positive integers |
| 49 | + if [[ $1 =~ ^[0-9]+$ ]] && [ $1 -gt 0 ] && [[ $2 =~ ^[0-9]+$ ]] && [ $2 -gt 0 ]; then |
| 50 | + NUM_COMMITS=$1 |
| 51 | + DAYS_AGO=$2 |
| 52 | + TIME_PERIOD=$DEFAULT_TIME_PERIOD |
| 53 | + echo "Will modify the last $NUM_COMMITS commits to $DAYS_AGO day(s) ago using $TIME_PERIOD period." |
| 54 | + else |
| 55 | + echo "Error: Both arguments must be positive integers." |
| 56 | + echo "Usage: $0 [number_of_commits] [days_ago] [time_period]" |
| 57 | + exit 1 |
| 58 | + fi |
| 59 | +elif [ $# -eq 3 ]; then |
| 60 | + # Validate first two arguments are positive integers and third is valid time period |
| 61 | + if [[ $1 =~ ^[0-9]+$ ]] && [ $1 -gt 0 ] && [[ $2 =~ ^[0-9]+$ ]] && [ $2 -gt 0 ]; then |
| 62 | + if [[ $3 == "morning" || $3 == "evening" || $3 == "both" ]]; then |
| 63 | + NUM_COMMITS=$1 |
| 64 | + DAYS_AGO=$2 |
| 65 | + TIME_PERIOD=$3 |
| 66 | + echo "Will modify the last $NUM_COMMITS commits to $DAYS_AGO day(s) ago using $TIME_PERIOD period." |
| 67 | + else |
| 68 | + echo "Error: Third argument must be 'morning', 'evening', or 'both'." |
| 69 | + echo "Usage: $0 [number_of_commits] [days_ago] [time_period]" |
| 70 | + exit 1 |
| 71 | + fi |
| 72 | + else |
| 73 | + echo "Error: First two arguments must be positive integers." |
| 74 | + echo "Usage: $0 [number_of_commits] [days_ago] [time_period]" |
| 75 | + exit 1 |
| 76 | + fi |
| 77 | +else |
| 78 | + echo "Error: Too many arguments." |
| 79 | + echo "Usage: $0 [number_of_commits] [days_ago] [time_period]" |
| 80 | + exit 1 |
| 81 | +fi |
| 82 | + |
| 83 | +# Get the target date in YYYY-MM-DD format |
| 84 | +# Check if we're on macOS or Linux |
| 85 | +if [[ "$(uname)" == "Darwin" ]]; then |
| 86 | + # macOS date command |
| 87 | + TARGET_DATE=$(date -v-${DAYS_AGO}d "+%Y-%m-%d") |
| 88 | +else |
| 89 | + # Linux date command |
| 90 | + TARGET_DATE=$(date -d "${DAYS_AGO} days ago" "+%Y-%m-%d") |
| 91 | +fi |
| 92 | + |
| 93 | +# Get current branch name |
| 94 | +CURRENT_BRANCH=$(git symbolic-ref --short HEAD) |
| 95 | +BACKUP_BRANCH="${CURRENT_BRANCH}_backup_$(date +%Y%m%d%H%M%S)" |
| 96 | + |
| 97 | +echo "Setting commit dates to $DAYS_AGO day(s) ago ($TARGET_DATE) with random times:" |
| 98 | +if [ "$TIME_PERIOD" == "morning" ]; then |
| 99 | + echo " - Between 5:00 AM and 8:00 AM" |
| 100 | +elif [ "$TIME_PERIOD" == "evening" ]; then |
| 101 | + echo " - Between 10:00 PM and midnight" |
| 102 | +else |
| 103 | + echo " - Between 5:00 AM and 8:00 AM" |
| 104 | + echo " - Between 10:00 PM and midnight" |
| 105 | + echo "Each commit will randomly choose one of these two time periods" |
| 106 | +fi |
| 107 | +echo "This will modify the last $NUM_COMMITS commits." |
| 108 | +echo "WARNING: This operation rewrites Git history and is destructive." |
| 109 | +echo "If you've already pushed these commits, you'll need to force push after this operation." |
| 110 | +echo "This may cause problems for other collaborators who have pulled these commits." |
| 111 | +echo "A backup branch named '$BACKUP_BRANCH' will be created." |
| 112 | +echo "Press Ctrl+C to cancel or Enter to continue..." |
| 113 | +read |
| 114 | + |
| 115 | +# Create backup branch |
| 116 | +echo "Creating backup branch '$BACKUP_BRANCH'..." |
| 117 | +git branch $BACKUP_BRANCH |
| 118 | + |
| 119 | +# Get the SHA of the commit before our target range |
| 120 | +LAST_COMMIT=$(git rev-parse HEAD~$NUM_COMMITS) |
| 121 | + |
| 122 | +# Get the list of commits to modify in reverse order (oldest first) |
| 123 | +COMMITS=$(git rev-list --reverse $LAST_COMMIT..HEAD) |
| 124 | + |
| 125 | +# Create a temporary file to store the filter script |
| 126 | +FILTER_SCRIPT=$(mktemp) |
| 127 | + |
| 128 | + |
| 129 | +# Start building the filter script |
| 130 | +echo '#!/bin/bash' > $FILTER_SCRIPT |
| 131 | +echo '' >> $FILTER_SCRIPT |
| 132 | + |
| 133 | +# Process each commit |
| 134 | +for COMMIT in $COMMITS; do |
| 135 | + # Generate random time based on selected time period |
| 136 | + if [ "$TIME_PERIOD" == "morning" ]; then |
| 137 | + # Morning period: 5:00 AM - 8:00 AM (hours 5-7) |
| 138 | + RANDOM_HOUR=$((RANDOM % 3 + 5)) # Hours 5, 6, 7 |
| 139 | + elif [ "$TIME_PERIOD" == "evening" ]; then |
| 140 | + # Evening period: 10:00 PM - midnight (hours 22-23) |
| 141 | + RANDOM_HOUR=$((RANDOM % 2 + 22)) # Hours 22, 23 |
| 142 | + else |
| 143 | + # Both periods: randomly choose between morning and evening |
| 144 | + if [ $((RANDOM % 2)) -eq 0 ]; then |
| 145 | + # Morning period: 5:00 AM - 8:00 AM |
| 146 | + RANDOM_HOUR=$((RANDOM % 3 + 5)) # Hours 5, 6, 7 |
| 147 | + else |
| 148 | + # Evening period: 10:00 PM - midnight |
| 149 | + RANDOM_HOUR=$((RANDOM % 2 + 22)) # Hours 22, 23 |
| 150 | + fi |
| 151 | + fi |
| 152 | + |
| 153 | + # Generate random minutes and seconds |
| 154 | + RANDOM_MINUTE=$((RANDOM % 60)) |
| 155 | + RANDOM_SECOND=$((RANDOM % 60)) |
| 156 | + |
| 157 | + # Format the random time as HH:MM:SS |
| 158 | + RANDOM_TIME=$(printf "%02d:%02d:%02d" $RANDOM_HOUR $RANDOM_MINUTE $RANDOM_SECOND) |
| 159 | + COMMIT_DATE="${TARGET_DATE}T${RANDOM_TIME}" |
| 160 | + |
| 161 | + # Add this commit to the filter script |
| 162 | + echo "if [ \$GIT_COMMIT = '$COMMIT' ]; then" >> $FILTER_SCRIPT |
| 163 | + echo " export GIT_AUTHOR_DATE=\"$COMMIT_DATE\"" >> $FILTER_SCRIPT |
| 164 | + echo " export GIT_COMMITTER_DATE=\"$COMMIT_DATE\"" >> $FILTER_SCRIPT |
| 165 | + echo "fi" >> $FILTER_SCRIPT |
| 166 | + echo "" >> $FILTER_SCRIPT |
| 167 | + |
| 168 | + echo "Will set commit $COMMIT to $COMMIT_DATE" |
| 169 | +done |
| 170 | + |
| 171 | +# Make the filter script executable |
| 172 | +chmod +x $FILTER_SCRIPT |
| 173 | + |
| 174 | +# Run git filter-branch with the filter script |
| 175 | +echo "Rewriting commit dates..." |
| 176 | +git filter-branch -f --env-filter "source $FILTER_SCRIPT" $LAST_COMMIT..HEAD |
| 177 | + |
| 178 | +# Remove the temporary filter script |
| 179 | +rm $FILTER_SCRIPT |
| 180 | + |
| 181 | +echo -n "Done! The last $NUM_COMMITS commits now have their dates set to $DAYS_AGO day(s) ago with random times " |
| 182 | +if [ "$TIME_PERIOD" == "morning" ]; then |
| 183 | + echo "between 5:00 AM - 8:00 AM." |
| 184 | +elif [ "$TIME_PERIOD" == "evening" ]; then |
| 185 | + echo "between 10:00 PM - midnight." |
| 186 | +else |
| 187 | + echo "from two periods: 5:00 AM - 8:00 AM or 10:00 PM - midnight." |
| 188 | +fi |
| 189 | +echo "A backup of the original state was created in branch: $BACKUP_BRANCH" |
| 190 | +echo "" |
| 191 | +echo "To push these changes to the remote repository, use: git push --force" |
| 192 | +echo "To restore the original state, use: git reset --hard $BACKUP_BRANCH" |
0 commit comments