Instantly share code, notes, and snippets.
Last active
March 5, 2025 17:04
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save jerodg/c1b9da7c3beb60c702f17dc34397bf01 to your computer and use it in GitHub Desktop.
Git Author Rewrite
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# ---------------------------------------------------------------------- | |
# Author: JerodG <https://github.com/jerodg> | |
# File: reauthor.sh | |
# | |
# Purpose: Updates git commit history in multiple repositories by: | |
# - Changing author and committer details | |
# - Adding GPG signing information | |
# - Rebasing and re-signing all commits | |
# | |
# This script processes all git repositories in the current location | |
# and requires git-filter-repo to be installed. | |
# ---------------------------------------------------------------------- | |
# Configuration | |
GIT_AUTHOR_NAME="" | |
GIT_AUTHOR_EMAIL="" | |
GIT_COMMITTER_NAME="" | |
GIT_COMMITTER_EMAIL="" | |
GIT_SIGNING_KEY="" | |
# Check that configuration variables have been set | |
if [ -z "$GIT_AUTHOR_NAME" ] || [ -z "$GIT_AUTHOR_EMAIL" ] || [ -z "$GIT_COMMITTER_NAME" ] || [ -z "$GIT_COMMITTER_EMAIL" ] || [ -z "$GIT_SIGNING_KEY" ]; then | |
echo "Please configure the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, and GIT_SIGNING_KEY variables before running this script." | |
exit 1 | |
fi | |
# Color codes for terminal output | |
RED='\033[0;31m' | |
GREEN='\033[0;32m' | |
YELLOW='\033[0;33m' | |
BLUE='\033[0;34m' | |
NC='\033[0m' # No Color | |
# Log messages with appropriate colors | |
log_info() { | |
echo -e "${BLUE}[INFO]${NC} $1" | |
} | |
log_success() { | |
echo -e "${GREEN}[SUCCESS]${NC} $1" | |
} | |
log_warning() { | |
echo -e "${YELLOW}[WARNING]${NC} $1" | |
} | |
log_error() { | |
echo -e "${RED}[ERROR]${NC} $1" | |
} | |
# Error handling function | |
handle_error() { | |
log_error "$1" | |
exit "${2:-1}" # Default to exit code 1 if not specified | |
} | |
# Verify git-filter-repo availability before proceeding | |
# Exit early if the required tool isn't available | |
if ! command -v git-filter-repo &> /dev/null | |
then | |
handle_error "git-filter-repo could not be found, please install it first." 2 | |
fi | |
# Find all directories that are git repositories | |
folders=$(find . -maxdepth 1 -type d -name "zs.*" -exec test -e "{}/.git" ';' -print) | |
# Validate that at least one matching directory was found | |
# Exit early if no directories match the pattern | |
if [ -z "$folders" ]; then | |
handle_error "No git repositories found." 3 | |
fi | |
# Count the number of folders for progress tracking | |
total_folders=$(echo "$folders" | wc -l) | |
current_folder=0 | |
# Process each matching directory | |
for folder in $folders; do | |
current_folder=$((current_folder + 1)) | |
log_info "Processing folder: $folder ($current_folder of $total_folders)" | |
# Navigate to target repository | |
cd "$folder" || handle_error "Failed to enter directory: $folder" 4 | |
# Count total commits for progress reporting | |
total_commits=$(git rev-list --count --all 2>/dev/null) | |
if [ $? -ne 0 ]; then | |
log_warning "Could not count commits in $folder. Continuing anyway." | |
total_commits="unknown" | |
else | |
log_info "Found $total_commits commits to process" | |
fi | |
# Modify the commit history: | |
# - Updates author and committer information | |
# - Sets GPG signing key | |
# - Preserves original commit timestamps | |
log_info "Rewriting commit history with new identity..." | |
if ! git filter-repo --force \ | |
--commit-callback ' | |
commit.author_name = b"'"$GIT_AUTHOR_NAME"'" | |
commit.author_email = b"'"$GIT_AUTHOR_EMAIL"'" | |
commit.committer_name = b"'"$GIT_COMMITTER_NAME"'" | |
commit.committer_email = b"'"$GIT_COMMITTER_EMAIL"'" | |
commit.signing_key = b"'"$GIT_SIGNING_KEY"'" | |
commit.committer_date = commit.committer_date | |
commit.author_date = commit.author_date | |
' > /dev/null 2>&1; then | |
log_error "Failed to modify commit history in $folder. Continuing with next repository." | |
cd .. || handle_error "Failed to return to parent directory" 5 | |
continue | |
fi | |
# Re-sign all commits with the configured GPG key | |
log_info "Re-signing all commits with GPG key..." | |
if ! git rebase --exec 'git commit --amend --no-edit -n -S' --root; then | |
log_error "Failed to re-sign commits in $folder. Manual intervention may be required." | |
# Creating a recovery script for this repository | |
echo "#!/bin/bash" > "../recover_$folder.sh" | |
echo "cd $folder" >> "../recover_$folder.sh" | |
echo "git rebase --abort" >> "../recover_$folder.sh" | |
chmod +x "../recover_$folder.sh" | |
log_warning "Created recovery script: recover_$folder.sh" | |
else | |
log_success "Successfully processed $folder" | |
fi | |
# Return to the parent directory before processing the next repository | |
cd .. || handle_error "Failed to return to parent directory" 5 | |
done | |
# Final status report | |
if [ $current_folder -eq $total_folders ]; then | |
log_success "All $total_folders repositories have been processed." | |
else | |
log_warning "Processed $current_folder out of $total_folders repositories." | |
fi | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment