Sunday, 21 July 2024

Git fix all "missing" renames

To continue the previous epopee: we shall interactively detect all the occurrences of the missed renames in a given git repo, and let user choose if he wants to make a potential rename into real rename.

#!/bin/bash

# Check if correct number of arguments are provided
if [ $# -ne 1 ]; then
    echo "Usage: $0 <repo_path>"
    exit 1
fi

REPO_PATH="$1"

# Change to the repository directory
cd "$REPO_PATH" || exit 1

# Function to normalize filename
normalize_filename() {
    echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '_'
}

# Function to get the list of commits
get_commits() {
    git rev-list HEAD
}

# Get initial list of commits
COMMITS=$(get_commits)

needs_repetition=true

# Iterate through each commit
while [ "$needs_repetition" = true ]; do
    needs_repetition=false
    for COMMIT_HASH in $COMMITS; do
        echo -e "Currently observing commit: $COMMIT_HASH"

        # Get the next commit hash
        NEXT_COMMIT_HASH=$(git rev-list "$COMMIT_HASH" | grep -A 1 "$COMMIT_HASH" | tail -n 1)

        # Get list of added files for the current commit
        ADDED_FILES=$(git diff-tree --no-commit-id --name-status -r "$COMMIT_HASH" | grep "^A" | cut -f2-)

        # Remove added files that are seen as renames
        RENAMED_FILES=$(git diff-tree --no-commit-id --name-status -M -r "$COMMIT_HASH" | grep "^R" | cut -f3-)
        for renamed_file in $RENAMED_FILES; do
            ADDED_FILES=$(echo "$ADDED_FILES" | grep -v "^$renamed_file$")
        done

        # Get list of deleted files for the current commit
        DELETED_FILES=$(git diff-tree --no-commit-id --name-status -r "$COMMIT_HASH" | grep "^D" | cut -f2-)

        # Iterate through added files
        while IFS= read -r added_file; do
            [ -z "$added_file" ] && continue
            added_dir=$(dirname "$added_file")
            added_name=$(basename "$added_file")
            added_norm=$(normalize_filename "${added_name%.*}")

            # Iterate through deleted files
            while IFS= read -r deleted_file; do
                [ -z "$deleted_file" ] && continue
                deleted_dir=$(dirname "$deleted_file")
                deleted_name=$(basename "$deleted_file")
                deleted_norm=$(normalize_filename "${deleted_name%.*}")

                # Check if files are in the same directory and have matching normalized names
                if [ "$deleted_dir" = "$added_dir" ] && [ "$deleted_norm" = "$added_norm" ]; then
                    echo -e "\e[93mCOMMIT_HASH=$COMMIT_HASH, OLD_FILE=$deleted_file, NEW_FILE=$added_file\e[0m"
                    
                    # Prompt user for confirmation
                    echo -e "\e[94mDo you want to run fix_renames.sh for this match? (Y/N)\e[0m"
                                        
                    read -r response < /dev/tty
                    
                    if [[ "$response" =~ ^[Yy]$ ]]; then
                        cd ..
                        ./fix_renames.sh "$REPO_PATH" "$COMMIT_HASH" "$deleted_file" "$added_file"
                        cd "$REPO_PATH"
                        
                        # Recreate the list of commits
                        COMMITS=$(get_commits)
                        
                        # Find the position of the next commit hash
                        NEXT_COMMIT_POSITION=$(echo "$COMMITS" | grep -n "$NEXT_COMMIT_HASH" | cut -d: -f1)
                        
                        # Continue from the commit preceding the next commit
                        COMMITS=$(echo "$COMMITS" | tail -n +"$((NEXT_COMMIT_POSITION - 1))")
                        
                        NEW_COMMIT_HASH=$(echo "$COMMITS" | head -n 1)
                        
                        echo -e "\e[92mThe old commit $COMMIT_HASH now became the new commit $NEW_COMMIT_HASH expected to be followed by $NEXT_COMMIT_HASH.\e[0m"
                        
                        needs_repetition=true
                        break 3
                    fi
                fi
            done <<< "$DELETED_FILES"
        done <<< "$ADDED_FILES"
    done

    if ! $needs_repetition; then
        break
    fi
done

set FILTER_BRANCH_SQUELCH_WARNING=1

git filter-branch --env-filter 'GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE; export GIT_COMMITTER_DATE' -- --branches --tags

No comments:

Post a Comment