diff --git a/gp/gp b/gp/gp index b68f73b..a91925c 100755 --- a/gp/gp +++ b/gp/gp @@ -1,18 +1,322 @@ #!/bin/bash -# find top folder of the git repo from current directory +# gp - Git Push with AI-generated commit messages +# Improved version with better error handling, safety checks, and functionality -MYDIR=$(git rev-parse --show-toplevel) +set -euo pipefail -if [ -z "$MYDIR" ]; then - echo "Not in a git repository" - exit 1 -fi +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color -# do a git diff, and use AI to determine a good commit message. +# 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"; } -# add all changes at $MYDIR level, commit with the message from AI, and push to origin. +# Function to show help +show_help() { + cat << 'EOF' +gp - Git Push with smart commit messages -git add . -git commit -m "$(git diff --name-only | xargs -I{} sh -c 'echo "{}: "; git diff --color=always {} | head -n 1')" -git push origin main +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 diff --git a/gp/gp_improved b/gp/gp_improved deleted file mode 100755 index a91925c..0000000 --- a/gp/gp_improved +++ /dev/null @@ -1,322 +0,0 @@ -#!/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