diff --git a/gp/IMPROVEMENTS.md b/gp/IMPROVEMENTS.md new file mode 100644 index 0000000..31d6bae --- /dev/null +++ b/gp/IMPROVEMENTS.md @@ -0,0 +1,91 @@ +# GP Script Improvements + +## Original Script Issues + +The original `gp` script had several limitations and potential issues: + +1. **No error handling** - Script could fail silently or in unexpected ways +2. **Unsafe operations** - Always commits and pushes without confirmation +3. **Poor commit messages** - Generated from diff first line, often meaningless +4. **No flexibility** - Hard-coded to push to `main` branch +5. **No dry-run mode** - No way to preview what would happen +6. **Limited functionality** - No help, version, or autocomplete support +7. **Poor user experience** - No colored output or informative messages + +## Improvements Made + +### 1. Safety and Error Handling +- **Strict error handling** with `set -euo pipefail` +- **Git repository validation** before any operations +- **Change detection** to avoid empty commits +- **User confirmation** before committing (unless `--force` used) +- **Proper error messages** with colored output + +### 2. Enhanced Functionality +- **Dry-run mode** (`--dry-run`/`-n`) to preview changes +- **Custom commit messages** - optional argument overrides auto-generation +- **Branch selection** (`--branch`/`-b`) to push to any branch +- **Add-all option** (`--add-all`/`-a`) to include untracked files +- **Force mode** (`--force`/`-f`) to skip confirmations + +### 3. Smart Commit Message Generation +- **File type detection** - recognizes source, config, docs, and test files +- **Change type analysis** - detects additions, modifications, deletions +- **Conventional commit style** - uses prefixes like `feat:`, `docs:`, `test:` +- **Intelligent descriptions** - better context than original script + +### 4. User Experience +- **Colored output** - different colors for info, warnings, errors, success +- **Comprehensive help** - detailed usage information and examples +- **Status display** - shows current branch, repository, and changes +- **Progress feedback** - clear indication of what's happening + +### 5. Integration Features +- **Version command** - reports script version +- **Autocomplete support** - bash completion for options +- **Standard CLI conventions** - follows common command-line patterns + +## Usage Examples + +### Basic Usage +```bash +gp # Auto-generate commit message and push +gp "Fix parser bug" # Use custom commit message +``` + +### Advanced Usage +```bash +gp --dry-run # Preview what would be committed +gp --add-all # Include untracked files +gp --branch develop # Push to develop branch +gp --force "Emergency fix" # Skip confirmation prompt +``` + +### Information Commands +```bash +gp --help # Show help +gp version # Show version +gp autocomplete # Show completion options +``` + +## Benefits + +1. **Safer** - Prevents accidental commits and pushes +2. **More flexible** - Works with different workflows and branching strategies +3. **Better commit history** - Generates meaningful commit messages +4. **User-friendly** - Clear feedback and error messages +5. **Professional** - Follows CLI best practices and conventions +6. **Maintainable** - Well-structured code with proper error handling + +## Backward Compatibility + +The improved script maintains backward compatibility: +- Running `gp` without arguments still auto-generates a commit message and pushes +- The basic workflow is unchanged +- New features are opt-in through command-line flags + +## Installation + +1. Replace the original `gp` script with `gp_improved` +2. Ensure it's executable: `chmod +x gp` +3. The script works from any directory within a git repository \ No newline at end of file diff --git a/gp/gp_improved b/gp/gp_improved new file mode 100755 index 0000000..a91925c --- /dev/null +++ b/gp/gp_improved @@ -0,0 +1,322 @@ +#!/bin/bash + +# gp - Git Push with AI-generated commit messages +# Improved version with better error handling, safety checks, and functionality + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored output +print_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } +print_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# Function to show help +show_help() { + cat << 'EOF' +gp - Git Push with smart commit messages + +USAGE: + gp [options] [message] + +OPTIONS: + -h, --help Show this help message + -n, --dry-run Show what would be committed without actually doing it + -f, --force Skip safety checks (use with caution) + -b, --branch Specify branch to push to (default: current branch) + -a, --add-all Add all files including untracked (default: only staged + modified) + +ARGUMENTS: + message Optional custom commit message (overrides auto-generation) + +EXAMPLES: + gp # Auto-generate commit message and push + gp "Fix bug in parser" # Use custom commit message + gp --dry-run # Preview what would be committed + gp -b develop # Push to develop branch instead of current + +EOF +} + +# Function to generate commit message based on changes +generate_commit_message() { + local files_changed=$(git diff --cached --name-only) + local files_count=$(echo "$files_changed" | wc -l) + + if [ -z "$files_changed" ]; then + files_changed=$(git diff --name-only) + files_count=$(echo "$files_changed" | wc -l) + fi + + # If add-all is enabled, also include untracked files + if [ "$ADD_ALL" = true ] && [ -z "$files_changed" ]; then + files_changed=$(git ls-files --others --exclude-standard) + files_count=$(echo "$files_changed" | wc -l) + fi + + if [ -z "$files_changed" ]; then + echo "No changes to commit" + return 1 + fi + + # Generate smart commit message based on file types and changes + local has_source_files=false + local has_config_files=false + local has_docs=false + local has_tests=false + local message="" + + while IFS= read -r file; do + [ -z "$file" ] && continue + + case "$file" in + *.cpp|*.hpp|*.c|*.h|*.js|*.ts|*.py|*.go|*.rs|*.java) + has_source_files=true + ;; + *.json|*.yml|*.yaml|*.toml|*.ini|*.conf|CMakeLists.txt|Makefile) + has_config_files=true + ;; + *.md|*.txt|*.rst|docs/*|README*) + has_docs=true + ;; + *test*|*spec*|test/*|tests/*) + has_tests=true + ;; + esac + done <<< "$files_changed" + + # Create descriptive commit message + if [ "$files_count" -eq 1 ]; then + local single_file=$(echo "$files_changed" | head -1) + local change_type=$(git diff --cached --name-status "$single_file" 2>/dev/null || git diff --name-status "$single_file") + case "${change_type:0:1}" in + A) message="Add $single_file" ;; + M) message="Update $single_file" ;; + D) message="Remove $single_file" ;; + R) message="Rename $single_file" ;; + *) message="Modify $single_file" ;; + esac + else + local prefix="" + if $has_tests; then + prefix="test: " + elif $has_docs; then + prefix="docs: " + elif $has_config_files; then + prefix="config: " + elif $has_source_files; then + prefix="feat: " + fi + + message="${prefix}Update $files_count files" + fi + + echo "$message" +} + +# Function to check if we're in a git repository +check_git_repo() { + if ! git rev-parse --git-dir >/dev/null 2>&1; then + print_error "Not in a git repository" + exit 1 + fi +} + +# Function to check for uncommitted changes +check_for_changes() { + if git diff --cached --quiet && git diff --quiet; then + if [ "$ADD_ALL" = false ]; then + print_warning "No staged changes found" + print_info "Use 'gp -a' to add all files, or stage changes first with 'git add'" + exit 0 + fi + fi +} + +# Function to show what would be committed +show_status() { + print_info "Current branch: $(git branch --show-current)" + print_info "Repository: $(git remote get-url origin 2>/dev/null || echo 'No remote')" + echo + + if git diff --cached --quiet; then + print_info "Staged changes: None" + if ! git diff --quiet; then + print_info "Modified files (unstaged):" + git diff --name-only | sed 's/^/ /' + fi + else + print_info "Staged changes:" + git diff --cached --name-only | sed 's/^/ /' + fi + + if [ "$ADD_ALL" = true ]; then + local untracked=$(git ls-files --others --exclude-standard) + if [ -n "$untracked" ]; then + print_info "Untracked files (will be added):" + echo "$untracked" | sed 's/^/ /' + fi + fi +} + +# Function to perform the actual commit and push +do_commit_and_push() { + local commit_msg="$1" + local target_branch="$2" + + # Add files if requested + if [ "$ADD_ALL" = true ]; then + print_info "Adding all files..." + git add . + fi + + # Commit + print_info "Committing with message: '$commit_msg'" + git commit -m "$commit_msg" + + # Push + print_info "Pushing to $target_branch..." + if git push origin "$target_branch"; then + print_success "Successfully pushed to origin/$target_branch" + else + print_error "Failed to push to origin/$target_branch" + print_info "You may need to pull first: git pull origin $target_branch" + exit 1 + fi +} + +# Default values +DRY_RUN=false +FORCE=false +ADD_ALL=false +CUSTOM_MESSAGE="" +TARGET_BRANCH="" + +# Handle special commands first +case "${1:-}" in + autocomplete) + shift + autocomplete "$@" + exit 0 + ;; + version) + echo "gp version 2.0.0" + exit 0 + ;; +esac + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -n|--dry-run) + DRY_RUN=true + shift + ;; + -f|--force) + FORCE=true + shift + ;; + -a|--add-all) + ADD_ALL=true + shift + ;; + -b|--branch) + TARGET_BRANCH="$2" + shift 2 + ;; + -*) + print_error "Unknown option: $1" + echo "Use 'gp --help' for usage information" + exit 1 + ;; + *) + CUSTOM_MESSAGE="$1" + shift + ;; + esac +done + +# Main execution +main() { + # Safety checks + check_git_repo + + # Set target branch if not specified + if [ -z "$TARGET_BRANCH" ]; then + TARGET_BRANCH=$(git branch --show-current) + fi + + # Check for changes + check_for_changes + + # Show current status + show_status + echo + + # Generate or use custom commit message + if [ -n "$CUSTOM_MESSAGE" ]; then + commit_message="$CUSTOM_MESSAGE" + print_info "Using custom commit message: '$commit_message'" + else + print_info "Generating commit message..." + commit_message=$(generate_commit_message) + if [ $? -ne 0 ]; then + print_error "Failed to generate commit message" + exit 1 + fi + print_info "Generated commit message: '$commit_message'" + fi + + # Dry run mode + if [ "$DRY_RUN" = true ]; then + print_warning "DRY RUN MODE - No changes will be made" + print_info "Would commit with message: '$commit_message'" + print_info "Would push to: origin/$TARGET_BRANCH" + exit 0 + fi + + # Safety confirmation (unless forced) + if [ "$FORCE" = false ]; then + echo + read -p "Proceed with commit and push? [y/N] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "Aborted by user" + exit 0 + fi + fi + + # Execute the commit and push + do_commit_and_push "$commit_message" "$TARGET_BRANCH" +} + +# Autocomplete function for bash +autocomplete() { + local args=("$@") + if [ ${#args[@]} -eq 0 ]; then + echo "--help" + echo "--dry-run" + echo "--force" + echo "--add-all" + echo "--branch" + echo "-h" + echo "-n" + echo "-f" + echo "-a" + echo "-b" + fi +} + +# Run main function +main "$@" \ No newline at end of file