Git Change PR Authors
Modify author and committer information for commits in a PR branch
Metadata
- Author: Ropean
- Version: 2.0.0
- Dependencies: git 2.0+, bash 4.0+
This script rewrites Git history! Force push required!
Code
bash
#!/bin/bash
#
# @title Git Change PR Authors
# @description Modify author and committer information for commits in a PR branch
# @author Ropean
# @version 2.0.0
# @license MIT
# @repository https://github.com/ropean/scripts
# @requires git 2.0+, bash 4.0+
# @note This script rewrites Git history! Force push required!
#
# @example
# Usage:
# ./git-change-pr-authors.sh <base-branch-or-commit>
# ./git-change-pr-authors.sh main
# ./git-change-pr-authors.sh 059027114dd2419b20ddcbdf409ff3da15d715aa
#
################################################################################
# Git Change PR Authors
################################################################################
#
################################################################################
# DESCRIPTION
################################################################################
#
# This script modifies the author and committer information for all commits
# from a specified base (branch or commit) to the current HEAD. It's designed
# for cleaning up author information in Pull Request branches before merging.
#
# Key Features:
# - Modifies only commits in the specified range (doesn't touch base branch)
# - Preserves commit messages and code changes
# - Automatically handles merge commits (skips them during rebase)
# - Creates automatic backup for easy rollback
# - Interactive confirmation with sensible defaults
# - Optional automatic push and cleanup
# - Comprehensive error handling
#
# Use Cases:
# - Standardize author information across a PR
# - Fix incorrect author/email in commits
# - Clean up commits before merging to main branch
# - Unify multiple authors to a single identity
#
################################################################################
# USAGE
################################################################################
#
# Syntax:
# ./git-change-pr-authors.sh <base-branch-or-commit>
#
# Examples:
# # Modify all commits after main branch
# ./git-change-pr-authors.sh main
#
# # Modify commits after a specific commit hash
# ./git-change-pr-authors.sh 059027114dd2419b20ddcbdf409ff3da15d715aa
#
# # Modify commits after develop branch
# ./git-change-pr-authors.sh develop
#
# # Modify last 3 commits
# ./git-change-pr-authors.sh HEAD~3
#
################################################################################
# CONFIGURATION
################################################################################
#
# Before using this script, modify the following variables to match your
# desired author information:
#
# NEW_AUTHOR_NAME - The name to use for all modified commits
# NEW_AUTHOR_EMAIL - The email to use for all modified commits
# NEW_COMMITTER_NAME - The committer name (usually same as author)
# NEW_COMMITTER_EMAIL - The committer email (usually same as author)
#
################################################################################
# Exit on error, undefined variables, and pipe failures
set -euo pipefail
################################################################################
# COLOR CODES FOR OUTPUT
################################################################################
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color
################################################################################
# CONFIGURATION VARIABLES
################################################################################
#
# ⚠️ MODIFY THESE VALUES BEFORE RUNNING THE SCRIPT
#
NEW_AUTHOR_NAME="your-name"
NEW_AUTHOR_EMAIL="your-email"
NEW_COMMITTER_NAME="your-name"
NEW_COMMITTER_EMAIL="your-email"
################################################################################
# UTILITY FUNCTIONS
################################################################################
#
# Print an informational message in green
# Arguments:
# $1 - Message to print
#
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
#
# Print an error message in red
# Arguments:
# $1 - Error message to print
#
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
#
# Print a warning message in yellow
# Arguments:
# $1 - Warning message to print
#
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
#
# Print a step/progress message in blue
# Arguments:
# $1 - Step message to print
#
print_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
#
# Display usage information and exit
# This function is called when invalid arguments are provided
#
show_usage() {
cat << EOF
Usage: $0 <base-branch-or-commit>
Description:
Modify author and committer information for all commits from the specified
base to the current HEAD. Only changes author information while preserving
commit messages and code changes.
Arguments:
base-branch-or-commit Branch name or commit hash to use as the base point.
Commits after this point will be modified.
Examples:
# Modify commits after main branch (most common use case)
$0 main
# Modify commits after a specific commit hash
$0 059027114dd2419b20ddcbdf409ff3da15d715aa
# Modify commits after develop branch
$0 develop
# Modify last 3 commits only
$0 HEAD~3
Notes:
- This script will rewrite Git history
- A backup branch is automatically created
- Merge commits are automatically skipped during rebase
- Force push is required to update remote branches
For more information, see the script header comments.
EOF
exit 1
}
################################################################################
# INPUT VALIDATION
################################################################################
# Check if exactly one argument is provided
if [ $# -ne 1 ]; then
print_error "Invalid number of arguments"
show_usage
fi
BASE_REF="$1"
################################################################################
# ENVIRONMENT CHECKS
################################################################################
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
print_error "Not a git repository"
print_error "Please run this script from within a Git repository"
exit 1
fi
# Validate that the base reference exists
if ! git rev-parse --verify "$BASE_REF" >/dev/null 2>&1; then
print_error "Invalid branch or commit: $BASE_REF"
print_error "Please provide a valid branch name or commit hash"
exit 1
fi
# Get current branch name
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
print_error "You are in detached HEAD state"
print_error "Please checkout a branch first: git checkout <branch-name>"
exit 1
fi
print_info "Current branch: $CURRENT_BRANCH"
# Check for uncommitted changes
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
print_error "You have uncommitted changes"
print_error "Please commit or stash them first:"
print_error " git stash"
print_error " # or"
print_error " git commit -am 'Your message'"
exit 1
fi
################################################################################
# COMMIT ANALYSIS
################################################################################
# Get the base commit hash
BASE_COMMIT=$(git rev-parse "$BASE_REF")
# Verify that base commit is an ancestor of current HEAD
# This ensures we're not trying to modify commits that aren't in our history
if ! git merge-base --is-ancestor "$BASE_COMMIT" HEAD 2>/dev/null; then
print_error "Base commit $BASE_REF is not an ancestor of current HEAD"
print_error "Cannot modify commits that are not in the current branch history"
print_error ""
print_error "Possible reasons:"
print_error " - The base branch/commit is ahead of your current branch"
print_error " - The base branch/commit is on a different branch line"
print_error ""
print_error "Please ensure you're on the correct branch and try again"
exit 1
fi
# Count total commits in the range
COMMIT_COUNT=$(git rev-list --count ${BASE_REF}..HEAD)
# Count merge commits (these will be skipped by rebase)
MERGE_COUNT=$(git rev-list --merges --count ${BASE_REF}..HEAD)
# Calculate non-merge commits (actual commits that will be modified)
NON_MERGE_COUNT=$((COMMIT_COUNT - MERGE_COUNT))
# Exit early if there are no commits to modify
if [ "$COMMIT_COUNT" -eq 0 ]; then
print_info "No commits to modify between $BASE_REF and HEAD"
print_info "Your branch is up to date with the base"
exit 0
fi
# Determine if BASE_REF is a branch or a commit hash
BASE_TYPE="commit"
if git show-ref --verify --quiet "refs/heads/$BASE_REF" 2>/dev/null; then
BASE_TYPE="branch"
fi
################################################################################
# DISPLAY INFORMATION AND CONFIRMATION
################################################################################
# Display summary of what will be modified
echo ""
print_step "Base: $BASE_REF ($BASE_TYPE)"
print_step "Base commit: ${BASE_COMMIT:0:7}"
echo ""
# Show different messages depending on whether there are merge commits
if [ "$MERGE_COUNT" -gt 0 ]; then
print_step "Total commits: $COMMIT_COUNT (including $MERGE_COUNT merge commit(s))"
print_step "Commits to be modified: $NON_MERGE_COUNT (merge commits will be skipped by rebase)"
else
print_step "Commits to be modified: $COMMIT_COUNT"
fi
echo ""
# Display the list of commits that will be affected
git log --oneline --format=" %h - %s (by %an <%ae>)" ${BASE_REF}..HEAD
echo ""
# Show what the new author information will be
print_warning "Will change all authors to:"
echo " Author: $NEW_AUTHOR_NAME <$NEW_AUTHOR_EMAIL>"
echo " Committer: $NEW_COMMITTER_NAME <$NEW_COMMITTER_EMAIL>"
echo ""
# Ask for user confirmation
# Default is 'yes' (just press Enter)
# Accepts: y, Y, yes, Yes, YES, or empty (Enter)
# Rejects: n, N, no, No, NO
read -p "Continue? (Y/n): " -r
echo
if [[ -z "$REPLY" ]] || [[ "$REPLY" =~ ^[Yy]([Ee][Ss])?$ ]]; then
print_info "Proceeding with modification..."
elif [[ "$REPLY" =~ ^[Nn]([Oo])?$ ]]; then
print_info "Operation cancelled by user"
exit 0
else
print_error "Invalid input: $REPLY"
print_error "Please enter 'y' or 'n'"
exit 1
fi
################################################################################
# BACKUP CREATION
################################################################################
# Create a backup branch with timestamp
# This allows easy rollback if something goes wrong
BACKUP_BRANCH="backup-pr-authors-$(date +%Y%m%d-%H%M%S)"
print_step "Creating backup branch: $BACKUP_BRANCH"
git branch "$BACKUP_BRANCH"
print_info "Backup created. You can restore with: git reset --hard $BACKUP_BRANCH"
################################################################################
# ENVIRONMENT SETUP FOR REBASE
################################################################################
# Export environment variables that Git will use for author/committer info
# These are used by 'git commit --amend --reset-author'
export GIT_AUTHOR_NAME="$NEW_AUTHOR_NAME"
export GIT_AUTHOR_EMAIL="$NEW_AUTHOR_EMAIL"
export GIT_COMMITTER_NAME="$NEW_COMMITTER_NAME"
export GIT_COMMITTER_EMAIL="$NEW_COMMITTER_EMAIL"
#
# Cleanup function to unset environment variables
# This ensures we don't pollute the environment
#
cleanup() {
unset GIT_AUTHOR_NAME
unset GIT_AUTHOR_EMAIL
unset GIT_COMMITTER_NAME
unset GIT_COMMITTER_EMAIL
}
# Register cleanup function to run on script exit
trap cleanup EXIT
################################################################################
# REBASE SCRIPT PREPARATION
################################################################################
# Create a temporary script that will be used as GIT_SEQUENCE_EDITOR
# This script automatically changes all 'pick' commands to 'edit' commands
# in the rebase todo list, allowing us to modify each commit
TEMP_SCRIPT=$(mktemp)
cat > "$TEMP_SCRIPT" << 'EOF'
#!/bin/bash
# Automatically change all 'pick' to 'edit' in the rebase todo list
# This allows us to modify each commit's author information
sed -i.bak 's/^pick /edit /' "$1"
EOF
chmod +x "$TEMP_SCRIPT"
#
# Enhanced cleanup function that also removes temporary script
#
cleanup_all() {
rm -f "$TEMP_SCRIPT"
cleanup
}
# Update trap to use enhanced cleanup
trap cleanup_all EXIT
################################################################################
# INTERACTIVE REBASE EXECUTION
################################################################################
print_step "Starting rebase to modify commits..."
echo ""
# Start interactive rebase using our temporary script as the editor
# The script will automatically mark all commits for editing
GIT_SEQUENCE_EDITOR="$TEMP_SCRIPT" git rebase -i "$BASE_REF" || {
print_error "Rebase failed to start"
print_error "Aborting rebase and cleaning up..."
git rebase --abort 2>/dev/null
exit 1
}
################################################################################
# COMMIT MODIFICATION LOOP
################################################################################
# Track how many commits we actually modify
MODIFIED_COUNT=0
# Process each commit in the rebase
while true; do
# Check if we're still in an active rebase
# The rebase-merge directory exists only during an active rebase
if [ ! -d "$(git rev-parse --git-dir)/rebase-merge" ]; then
# Rebase is complete
break
fi
# Amend the current commit with new author information
# --reset-author: Use the environment variables for author/committer
# --no-edit: Keep the existing commit message
# --no-verify: Skip pre-commit and commit-msg hooks
if git commit --amend --reset-author --no-edit --no-verify 2>/dev/null; then
MODIFIED_COUNT=$((MODIFIED_COUNT + 1))
CURRENT_COMMIT=$(git rev-parse --short HEAD)
echo " ✓ Modified commit $CURRENT_COMMIT"
fi
# Continue to the next commit in the rebase
if ! git rebase --continue 2>/dev/null; then
# Check if rebase is actually complete (not an error)
if [ ! -d "$(git rev-parse --git-dir)/rebase-merge" ]; then
# Rebase completed successfully
break
fi
# Actual error occurred
print_error "Failed to continue rebase"
print_error "Aborting rebase and cleaning up..."
git rebase --abort
exit 1
fi
done
################################################################################
# SUCCESS SUMMARY
################################################################################
echo ""
print_info "Successfully modified $MODIFIED_COUNT commits!"
print_warning "Commit hashes have changed due to history rewrite"
echo ""
# Display the modified commits with new author information
print_step "Modified commits:"
git log --format=" %h - %s (by %an <%ae>)" ${BASE_REF}..HEAD
echo ""
################################################################################
# POST-MODIFICATION OPTIONS
################################################################################
# Offer user options for next steps
print_warning "What would you like to do next?"
echo " 1. Push changes and delete backup (recommended)"
echo " 2. Just push changes (keep backup)"
echo " 3. Do nothing (manual operation)"
echo ""
read -p "Choose option (1/2/3, default=1): " -r
echo
# Default to option 1 if user just presses Enter
CHOICE="${REPLY:-1}"
case "$CHOICE" in
1)
# Option 1: Push and cleanup (most common workflow)
print_step "Pushing changes to remote..."
if git push --force-with-lease 2>&1; then
print_info "Successfully pushed changes"
print_step "Deleting backup branch..."
git branch -D "$BACKUP_BRANCH"
print_info "Backup branch deleted"
else
print_error "Push failed. Backup branch preserved: $BACKUP_BRANCH"
print_info "You can try pushing manually: git push --force-with-lease"
exit 1
fi
;;
2)
# Option 2: Push but keep backup (cautious approach)
print_step "Pushing changes to remote..."
if git push --force-with-lease 2>&1; then
print_info "Successfully pushed changes"
print_info "Backup branch preserved: $BACKUP_BRANCH"
print_warning "To delete backup later: git branch -D $BACKUP_BRANCH"
else
print_error "Push failed. Backup branch preserved: $BACKUP_BRANCH"
print_info "You can try pushing manually: git push --force-with-lease"
exit 1
fi
;;
3)
# Option 3: Manual operation (for advanced users)
print_info "No automatic actions taken"
echo ""
print_warning "Manual next steps:"
echo " 1. Review changes: git log --format=fuller"
echo " 2. Push changes: git push --force-with-lease"
echo " 3. If you need to undo: git reset --hard $BACKUP_BRANCH"
echo " 4. Delete backup when done: git branch -D $BACKUP_BRANCH"
;;
*)
# Invalid option
print_error "Invalid option: $CHOICE"
print_info "No actions taken. Backup preserved: $BACKUP_BRANCH"
echo ""
print_warning "Manual next steps:"
echo " 1. Push changes: git push --force-with-lease"
echo " 2. Delete backup: git branch -D $BACKUP_BRANCH"
exit 1
;;
esac
################################################################################
# COMPLETION
################################################################################
echo ""
print_info "Done! ✨"
echo ""
exit 0File Information
- Filename:
git-change-pr-authors.sh - Category: git
- Language: BASH