#!/usr/bin/env bash ################################################################################ # Revert Helper Script # # Purpose: Generate proper revert commit message and analyze safety # Version: 1.0.0 # Usage: ./revert-helper.sh # Returns: JSON with revert information # Exit Codes: # 0 = Success # 1 = Commit not found # 2 = Script execution error ################################################################################ set -euo pipefail ################################################################################ # Parse conventional commit message ################################################################################ parse_commit_message() { local subject="$1" local type="" local scope="" local description="" # Try to match conventional format: type(scope): description if [[ "$subject" =~ ^([a-z]+)(\([a-z0-9\-]+\)):[[:space:]](.+)$ ]]; then type="${BASH_REMATCH[1]}" scope="${BASH_REMATCH[2]}" # includes parentheses scope="${scope#(}" # remove leading ( scope="${scope%)}" # remove trailing ) description="${BASH_REMATCH[3]}" # Try to match without scope: type: description elif [[ "$subject" =~ ^([a-z]+):[[:space:]](.+)$ ]]; then type="${BASH_REMATCH[1]}" scope="" description="${BASH_REMATCH[2]}" else # Non-conventional format type="" scope="" description="$subject" fi echo "$type|$scope|$description" } ################################################################################ # Generate revert commit message ################################################################################ generate_revert_message() { local commit_sha="$1" local original_subject="$2" # Parse original message local parsed parsed=$(parse_commit_message "$original_subject") local type local scope local description IFS='|' read -r type scope description <<< "$parsed" # Build revert message local revert_subject if [[ -n "$type" ]]; then # Conventional format if [[ -n "$scope" ]]; then revert_subject="revert: $type($scope): $description" else revert_subject="revert: $type: $description" fi else # Non-conventional format revert_subject="revert: $original_subject" fi # Build full message (subject + body + footer) local revert_message revert_message=$(cat </dev/null || echo "") # Check for file overlap while IFS= read -r file; do if [[ -n "$file" ]] && echo "$files_changed" | grep -qxF "$file"; then dependent_commits+=("$commit_line") ((dependent_count++)) break fi done <<< "$later_files" done <<< "$later_commits" if [[ $dependent_count -gt 0 ]]; then safe_to_revert="false" warnings+=("\"$dependent_count commit(s) depend on this change\"") fi fi fi # Check if files still exist (if deleted, might be harder to revert) local deleted_files=0 while IFS= read -r file; do if [[ -n "$file" ]] && [[ ! -f "$file" ]]; then ((deleted_files++)) fi done <<< "$files_changed" if [[ $deleted_files -gt 0 ]]; then warnings+=("\"$deleted_files file(s) from commit no longer exist\"") fi # Check for potential merge conflicts (files modified since commit) local modified_files=0 while IFS= read -r file; do if [[ -n "$file" ]] && [[ -f "$file" ]]; then # Check if file has been modified since this commit local file_changed file_changed=$(git log "$commit_sha"..HEAD --oneline -- "$file" | wc -l) if [[ $file_changed -gt 0 ]]; then ((modified_files++)) fi fi done <<< "$files_changed" if [[ $modified_files -gt 0 ]]; then warnings+=("\"$modified_files file(s) modified since commit - potential conflicts\"") fi # Format warnings array local warnings_json="[]" if [[ ${#warnings[@]} -gt 0 ]]; then warnings_json="[$(IFS=,; echo "${warnings[*]}")]" fi echo "{\"safe_to_revert\": $safe_to_revert, \"warnings\": $warnings_json, \"dependent_count\": $dependent_count}" } ################################################################################ # Main execution ################################################################################ main() { # Check arguments if [[ $# -lt 1 ]]; then echo "{\"error\": \"Usage: revert-helper.sh \"}" exit 2 fi local commit_sha="$1" # Verify we're in a git repository if ! git rev-parse --git-dir &>/dev/null; then echo "{\"error\": \"Not a git repository\"}" exit 2 fi # Verify commit exists if ! git rev-parse --verify "$commit_sha" &>/dev/null 2>&1; then echo "{\"error\": \"Commit not found: $commit_sha\"}" exit 1 fi # Get full commit SHA local full_sha full_sha=$(git rev-parse "$commit_sha") local short_sha short_sha=$(git rev-parse --short "$commit_sha") # Get commit information local original_subject local original_author local commit_date local files_affected original_subject=$(git log -1 --format='%s' "$commit_sha") original_author=$(git log -1 --format='%an <%ae>' "$commit_sha") commit_date=$(git log -1 --format='%ad' --date=short "$commit_sha") files_affected=$(git show --name-only --format= "$commit_sha" | wc -l) # Parse commit type and scope local parsed parsed=$(parse_commit_message "$original_subject") local type local scope local description IFS='|' read -r type scope description <<< "$parsed" # Generate revert message local revert_message revert_message=$(generate_revert_message "$short_sha" "$original_subject") # Analyze safety local safety_analysis safety_analysis=$(analyze_revert_safety "$commit_sha") # Build JSON output cat <