diff --git a/README.md b/README.md index e69de29..3e810bc 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,304 @@ +# GP - Git Push with Smart Commit Messages + +A powerful, safe, and user-friendly Git workflow automation tool that intelligently generates commit messages and streamlines the commit-push process. + +## Features + +### 🚀 **Smart Commit Messages** +- **File type detection** - Recognizes source code, config files, documentation, and tests +- **Change analysis** - Detects additions, modifications, deletions, and renames +- **Conventional commits** - Uses prefixes like `feat:`, `docs:`, `test:`, `config:` +- **Intelligent descriptions** - Context-aware commit messages based on actual changes + +### 🛡️ **Safety First** +- **Preview mode** - `--dry-run` shows exactly what will happen before executing +- **User confirmation** - Always asks before adding files or pushing changes +- **Change detection** - Prevents empty commits and handles unpushed commits +- **Clear feedback** - Colored output with detailed status information + +### ⚡ **Workflow Enhancement** +- **Add-all default** - Automatically includes untracked and modified files +- **Unpushed commit handling** - Detects and offers to push existing commits +- **Branch flexibility** - Push to any branch with `--branch` option +- **Force mode** - Skip confirmations for automation scripts + +### 🎯 **Professional CLI** +- **Comprehensive help** - Built-in documentation with examples +- **Bash completion** - Tab completion for all options +- **Version tracking** - Semantic versioning support +- **Standard conventions** - Follows Unix CLI best practices + +## Installation + +1. **Download the script:** + ```bash + curl -o gp https://raw.githubusercontent.com/your-repo/gp/main/gp + chmod +x gp + ``` + +2. **Place in your PATH:** + ```bash + mv gp ~/.local/bin/ + # or + sudo mv gp /usr/local/bin/ + ``` + +3. **Verify installation:** + ```bash + gp version + ``` + +## Quick Start + +### Basic Usage +```bash +# Add all files, generate commit message, and push +gp + +# Use custom commit message +gp "Fix authentication bug in login module" + +# Preview what would happen without executing +gp --dry-run +``` + +### Example Workflow +```bash +$ gp --dry-run +[INFO] Current branch: main +[INFO] Repository: https://github.com/user/project + +[INFO] Modified files (will be added): + src/auth.py + tests/test_auth.py +[INFO] Untracked files (will be added): + docs/auth.md + +[WARNING] Files will be automatically added before committing +[INFO] Generated commit message: 'feat: Update 3 files' +[WARNING] DRY RUN MODE - No changes will be made +[INFO] Would commit with message: 'feat: Update 3 files' +[INFO] Would push to: origin/main +``` + +## Command Reference + +### Options + +| Option | Description | +|--------|-------------| +| `-h, --help` | Show help message with examples | +| `-n, --dry-run` | Preview changes without executing | +| `-f, --force` | Skip all confirmations | +| `-a, --add-all` | Add all files including untracked (default) | +| `--staged-only` | Only commit staged changes | +| `-b, --branch BRANCH` | Push to specified branch | + +### Commands + +| Command | Description | +|---------|-------------| +| `gp` | Basic usage with smart defaults | +| `gp "message"` | Custom commit message | +| `gp version` | Show version information | +| `gp autocomplete` | Show completion options | + +## Examples + +### Basic Operations +```bash +# Standard workflow - add all files and push +gp + +# Custom commit message +gp "Add user authentication feature" + +# Preview before executing +gp --dry-run + +# Push to different branch +gp --branch develop "Update feature branch" +``` + +### Advanced Usage +```bash +# Only commit staged files (don't add untracked) +gp --staged-only + +# Force mode (no confirmations) - good for scripts +gp --force "Automated update" + +# Combine options +gp --dry-run --branch feature/auth "Test authentication changes" +``` + +### Handling Different Scenarios + +#### When you have unpushed commits: +```bash +$ gp +[INFO] No staged changes, but found 2 unpushed commit(s) +[INFO] Latest unpushed commit: abc1234 Fix parser bug + +Push existing commits to origin/main? [y/N] y +[SUCCESS] Successfully pushed existing commits to origin/main +``` + +#### When you have mixed changes: +```bash +$ gp --staged-only +[INFO] Staged changes: + src/main.py +[INFO] Modified files (unstaged, will NOT be included): + README.md +[WARNING] Unstaged changes will NOT be included (use -a to include them) +``` + +## Smart Commit Message Examples + +GP analyzes your changes and generates meaningful commit messages: + +### Single File Changes +- `Add src/auth.py` - New file added +- `Update README.md` - Existing file modified +- `Remove old_config.json` - File deleted + +### Multiple Files by Type +- `feat: Update 3 files` - Source code changes +- `docs: Update 2 files` - Documentation changes +- `test: Update 4 files` - Test file changes +- `config: Update 2 files` - Configuration changes + +### File Type Detection +- **Source files**: `.py`, `.js`, `.cpp`, `.go`, `.rs`, etc. +- **Config files**: `.json`, `.yml`, `.toml`, `CMakeLists.txt`, etc. +- **Documentation**: `.md`, `.txt`, `.rst`, `README*`, `docs/*` +- **Tests**: `*test*`, `*spec*`, `test/*`, `tests/*` + +## Safety Features + +### Confirmation Prompts +```bash +$ gp +[INFO] Untracked files (will be added): + new_feature.py + tests/test_new_feature.py + +Add these files and continue? [y/N] _ +``` + +### Change Detection +- ✅ **Prevents empty commits** - Exits gracefully when no changes +- ✅ **Handles unpushed commits** - Offers to push existing commits +- ✅ **Validates git repository** - Ensures you're in a valid repo +- ✅ **Branch validation** - Confirms target branch exists + +### Error Handling +- Clear error messages with suggested solutions +- Graceful handling of network issues +- Proper exit codes for script integration + +## Configuration + +### Environment Variables +None required - GP works out of the box. + +### Git Configuration +GP respects your existing Git configuration: +- Uses your configured remote origin +- Respects Git ignore files +- Works with your existing Git hooks + +## Integration + +### Bash Completion +Add to your `.bashrc`: +```bash +# GP completion +_gp_completion() { + local cur="${COMP_WORDS[COMP_CWORD]}" + local opts=$(gp autocomplete) + COMPREPLY=($(compgen -W "$opts" -- "$cur")) +} +complete -F _gp_completion gp +``` + +### Git Aliases +Add to your `.gitconfig`: +```ini +[alias] + p = !gp + pd = !gp --dry-run + pf = !gp --force +``` + +### CI/CD Integration +```bash +# In your CI script +gp --force "Automated deployment [skip ci]" +``` + +## Troubleshooting + +### Common Issues + +#### "Not in a git repository" +- **Solution**: Run GP from within a Git repository +- **Check**: `git status` should work + +#### "No changes to commit" +- **Cause**: Working tree is clean +- **Check**: `git status` to see if there are any changes + +#### "Failed to push" +- **Cause**: Usually needs to pull first +- **Solution**: `git pull origin main` then try again + +#### VSCode showing shellcheck errors +- **Solution**: Restart VSCode or reload the shellcheck extension +- **Check**: Command line `shellcheck gp` should show no errors + +### Debug Mode +For verbose output, modify the script to set: +```bash +set -x # Add after the shebang line for debug output +``` + +## Contributing + +### Development +1. Fork the repository +2. Make your changes +3. Test with `shellcheck gp` +4. Submit a pull request + +### Testing +```bash +# Lint the script +shellcheck gp + +# Test basic functionality +./gp --dry-run + +# Test with various scenarios +echo "test" > test.txt && ./gp --dry-run +``` + +## License + +MIT License - see LICENSE file for details. + +## Version History + +- **v2.0.0** - Complete rewrite with safety features and smart commit messages +- **v1.0.0** - Basic git automation script + +## Support + +- **Issues**: Report bugs and feature requests on GitHub +- **Documentation**: This README and built-in help (`gp --help`) +- **Examples**: See the examples section above + +--- + +**Made with ❤️ for developers who value safety and efficiency in their Git workflow.** \ No newline at end of file diff --git a/gp b/gp new file mode 100755 index 0000000..cd050fd --- /dev/null +++ b/gp @@ -0,0 +1,645 @@ +#!/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) + --staged-only Only commit staged changes (don't add untracked files) + +ARGUMENTS: + message Optional custom commit message (overrides auto-generation) + +EXAMPLES: + gp # Add all files, auto-generate commit message and push + gp "Fix bug in parser" # Add all files with custom commit message + gp --dry-run # Preview what would be committed + gp --staged-only # Only commit staged changes (don't add untracked) + gp -b develop # Push to develop branch instead of current + +EOF +} + +# Function to generate commit message based on changes +generate_commit_message() { + # First check if we have staged changes + local has_staged_changes=false + if ! git diff --cached --quiet; then + has_staged_changes=true + fi + + # Determine which changes to analyze based on staging status and ADD_ALL setting + local status_command="" + if [ "$has_staged_changes" = true ]; then + status_command="git diff --cached --name-status" + else + status_command="git diff --name-status" + fi + + # Get all changes (staged or unstaged depending on context) + local all_changes + all_changes=$($status_command) + + # If no changes from diff, check for untracked files when add-all is enabled + if [ -z "$all_changes" ] && [ "$ADD_ALL" = true ]; then + local untracked_files + untracked_files=$(git ls-files --others --exclude-standard) + if [ -n "$untracked_files" ]; then + # Convert untracked files to "A" (added) status format + all_changes=$(echo "$untracked_files" | sed 's/^/A\t/') + fi + fi + + if [ -z "$all_changes" ]; then + echo "No changes to commit" + return 1 + fi + + # Count total files + local files_count + files_count=$(echo "$all_changes" | wc -l) + + # 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="" + + # Extract just the filenames for type detection + while IFS=$'\t' read -r status 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/*|tests/*|*test*|*spec*) + has_tests=true + ;; + esac + done <<< "$all_changes" + + # Create descriptive commit message + if [ "$files_count" -eq 1 ]; then + local change_line + change_line=$(echo "$all_changes" | head -1) + local status + local single_file + status=$(echo "$change_line" | cut -f1) + single_file=$(echo "$change_line" | cut -f2) + + case "${status: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 + # For multiple files, analyze the types of changes + local added_count=0 + local modified_count=0 + local deleted_count=0 + local renamed_count=0 + + # Use the all_changes variable we already have + + # Count different types of changes + while IFS=$'\t' read -r status file; do + [ -z "$status" ] && continue + case "${status:0:1}" in + A) ((added_count++)) ;; + M) ((modified_count++)) ;; + D) ((deleted_count++)) ;; + R) ((renamed_count++)) ;; + esac + done <<< "$all_changes" + + # Also count untracked files if add-all is enabled + if [ "$ADD_ALL" = true ]; then + local untracked_files + untracked_files=$(git ls-files --others --exclude-standard) + if [ -n "$untracked_files" ]; then + local untracked_count + untracked_count=$(echo "$untracked_files" | wc -l) + ((added_count += untracked_count)) + fi + fi + + # Generate message based on change types + local change_parts=() + [ $added_count -gt 0 ] && change_parts+=("add $added_count") + [ $modified_count -gt 0 ] && change_parts+=("update $modified_count") + [ $deleted_count -gt 0 ] && change_parts+=("remove $deleted_count") + [ $renamed_count -gt 0 ] && change_parts+=("rename $renamed_count") + + local change_desc="" + if [ ${#change_parts[@]} -eq 1 ]; then + change_desc="${change_parts[0]}" + elif [ ${#change_parts[@]} -eq 2 ]; then + change_desc="${change_parts[0]} and ${change_parts[1]}" + else + # Join all but last with commas, last with "and" + local last_idx=$((${#change_parts[@]} - 1)) + for i in $(seq 0 $((last_idx - 1))); do + [ $i -gt 0 ] && change_desc+=", " + change_desc+="${change_parts[i]}" + done + change_desc+=" and ${change_parts[last_idx]}" + fi + + 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 + + # Capitalize first letter of change description + change_desc="$(echo "${change_desc:0:1}" | tr '[:lower:]' '[:upper:]')${change_desc:1}" + + message="${prefix}${change_desc} files" + fi + + echo "$message" +} + +# Function to check if we're in a git repository and change to repo root +check_git_repo() { + if ! git rev-parse --git-dir >/dev/null 2>&1; then + print_error "Not in a git repository" + exit 1 + fi + + # Change to the git repository root to ensure we operate on the entire repo + local git_root + git_root=$(git rev-parse --show-toplevel) + if [ "$PWD" != "$git_root" ]; then + print_info "Changing to git repository root: $git_root" + cd "$git_root" || { + print_error "Failed to change to git repository root" + exit 1 + } + fi +} + +# Function to check for uncommitted changes and unpushed commits +check_for_changes() { + local has_staged=false + local has_modified=false + local has_untracked=false + + # Check for staged changes + if ! git diff --cached --quiet; then + has_staged=true + fi + + # Check for modified files + if ! git diff --quiet; then + has_modified=true + fi + + # Check for untracked files + if [ -n "$(git ls-files --others --exclude-standard)" ]; then + has_untracked=true + fi + + # Check for unpushed commits + local current_branch + current_branch=$(git branch --show-current) + local unpushed_commits="" + if git rev-parse --verify "origin/$current_branch" >/dev/null 2>&1; then + unpushed_commits=$(git rev-list "origin/$current_branch..HEAD" -- 2>/dev/null || true) + fi + + # If add-all is enabled, check if we have any changes at all + if [ "$ADD_ALL" = true ]; then + if [ "$has_staged" = false ] && [ "$has_modified" = false ] && [ "$has_untracked" = false ]; then + # No working tree changes, but check for unpushed commits + if [ -n "$unpushed_commits" ]; then + local commit_count + commit_count=$(echo "$unpushed_commits" | wc -l) + print_info "No working tree changes, but found $commit_count unpushed commit(s)" + print_info "Latest unpushed commit: $(git log --oneline -1)" + + # Set a flag to indicate we should only push, not commit + PUSH_ONLY=true + return 0 + else + print_info "No changes to commit (working tree clean)" + exit 0 + fi + fi + else + # If add-all is disabled, only check staged changes + if [ "$has_staged" = false ]; then + # No staged changes, but check for unpushed commits + if [ -n "$unpushed_commits" ]; then + local commit_count + commit_count=$(echo "$unpushed_commits" | wc -l) + print_info "No staged changes, but found $commit_count unpushed commit(s)" + print_info "Latest unpushed commit: $(git log --oneline -1)" + + if [ "$has_modified" = true ] || [ "$has_untracked" = true ]; then + print_warning "You have unstaged changes that won't be included" + print_info "Use 'gp -a' to include all changes, or stage them first with 'git add'" + fi + + # Set a flag to indicate we should only push, not commit + PUSH_ONLY=true + return 0 + else + if [ "$has_modified" = true ] || [ "$has_untracked" = true ]; then + print_warning "No staged changes found" + print_info "Use 'gp -a' to add all files, or stage changes first with 'git add'" + else + print_info "No changes to commit (working tree clean)" + fi + exit 0 + fi + fi + fi +} + +# Function to show what would be committed and get user confirmation +show_status_and_confirm() { + print_info "Current branch: $(git branch --show-current)" + print_info "Repository: $(git remote get-url origin 2>/dev/null || echo 'No remote')" + echo + + local has_staged_changes=false + local has_unstaged_changes=false + local has_untracked_files=false + + # Show staged changes + if ! git diff --cached --quiet; then + local staged_modified="" + local staged_deleted="" + local staged_added="" + + # Get staged file status and categorize + while IFS=$'\t' read -r status file; do + [ -z "$status" ] && continue + case "${status:0:1}" in + A) staged_added="${staged_added}${file}\n" ;; + M) staged_modified="${staged_modified}${file}\n" ;; + D) staged_deleted="${staged_deleted}${file}\n" ;; + *) staged_modified="${staged_modified}${file}\n" ;; # Default to modified for other statuses + esac + done < <(git diff --cached --name-status) + + # Show staged added files + if [ -n "$staged_added" ]; then + print_info "Staged new files:" + echo -e "$staged_added" | grep -v '^$' | while IFS= read -r line; do echo " $line"; done + fi + + # Show staged modified files + if [ -n "$staged_modified" ]; then + print_info "Staged modified files:" + echo -e "$staged_modified" | grep -v '^$' | while IFS= read -r line; do echo " $line"; done + fi + + # Show staged deleted files + if [ -n "$staged_deleted" ]; then + print_info "Staged deleted files:" + echo -e "$staged_deleted" | grep -v '^$' | while IFS= read -r line; do echo " $line"; done + fi + + has_staged_changes=true + fi + + # Show unstaged changes + if ! git diff --quiet; then + local modified_files="" + local deleted_files="" + + # Get file status and categorize + while IFS=$'\t' read -r status file; do + [ -z "$status" ] && continue + case "${status:0:1}" in + M) modified_files="${modified_files}${file}\n" ;; + D) deleted_files="${deleted_files}${file}\n" ;; + *) modified_files="${modified_files}${file}\n" ;; # Default to modified for other statuses + esac + done < <(git diff --name-status) + + # Show modified files + if [ -n "$modified_files" ]; then + if [ "$ADD_ALL" = true ]; then + print_info "Modified files (will be added):" + else + print_info "Modified files (unstaged, will NOT be included):" + fi + echo -e "$modified_files" | grep -v '^$' | while IFS= read -r line; do echo " $line"; done + fi + + # Show deleted files + if [ -n "$deleted_files" ]; then + if [ "$ADD_ALL" = true ]; then + print_info "Deleted files (will be removed):" + else + print_info "Deleted files (unstaged, will NOT be included):" + fi + echo -e "$deleted_files" | grep -v '^$' | while IFS= read -r line; do echo " $line"; done + fi + + has_unstaged_changes=true + fi + + # Show untracked files + if [ "$ADD_ALL" = true ]; then + local untracked + untracked=$(git ls-files --others --exclude-standard) + if [ -n "$untracked" ]; then + print_info "Untracked files (will be added):" + # Use while loop to add indent without sed + while IFS= read -r line; do + echo " $line" + done <<< "$untracked" + has_untracked_files=true + fi + fi + + # Show summary of what will happen + echo + if [ "$ADD_ALL" = true ]; then + if [ "$has_unstaged_changes" = true ] || [ "$has_untracked_files" = true ]; then + print_warning "Files will be automatically added before committing" + fi + if [ "$has_staged_changes" = false ] && [ "$has_unstaged_changes" = false ] && [ "$has_untracked_files" = false ]; then + print_info "No changes found to commit" + fi + else + if [ "$has_unstaged_changes" = true ]; then + print_warning "Unstaged changes will NOT be included (use -a to include them)" + fi + if [ "$has_staged_changes" = false ]; then + print_info "No staged changes to commit" + fi + fi + + # Confirmation for file additions (unless forced or dry-run) + if [ "$ADD_ALL" = true ] && [ "$FORCE" = false ] && [ "$DRY_RUN" = false ]; then + if [ "$has_unstaged_changes" = true ] || [ "$has_untracked_files" = true ]; then + echo + read -p "Add these files and continue? [y/N] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "Aborted by user" + exit 0 + fi + 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 -A + fi + + # Commit + print_info "Committing with message: '$commit_msg'" + git commit -m "$commit_msg" -- + + # Push + print_info "Pushing to $target_branch..." + # Check if branch exists on remote to determine if we need -u flag + if git ls-remote --exit-code --heads origin "$target_branch" >/dev/null 2>&1; then + # Branch exists on remote, normal push + 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 + else + # Branch doesn't exist on remote, use -u to set upstream + print_info "Setting upstream branch (first push to origin/$target_branch)" + if git push -u origin "$target_branch"; then + print_success "Successfully pushed and set upstream to origin/$target_branch" + else + print_error "Failed to push to origin/$target_branch" + exit 1 + fi + fi +} + +# Autocomplete function for bash +autocomplete() { + local args=("$@") + if [ ${#args[@]} -eq 0 ]; then + printf "%s\n" "--help" "--dry-run" "--force" "--add-all" "--staged-only" "--branch" "-h" "-n" "-f" "-a" "-b" + fi +} + +# Default values +DRY_RUN=false +FORCE=false +ADD_ALL=true # Default to adding all files +CUSTOM_MESSAGE="" +TARGET_BRANCH="" +PUSH_ONLY=false + +# Handle special commands first +case "${1:-}" in + autocomplete) + shift + autocomplete "$@" + exit 0 + ;; + version) + echo "2025.0802.1112" + 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 # Explicitly set (though it's default) + shift + ;; + --staged-only) + ADD_ALL=false + shift + ;; + -b|--branch) + if [ -z "${2:-}" ]; then + print_error "Option -b/--branch requires a branch name" + exit 1 + fi + 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 + + # Handle push-only case (unpushed commits but no working tree changes) + if [ "$PUSH_ONLY" = true ]; then + echo + + # Dry run mode for push-only + if [ "$DRY_RUN" = true ]; then + print_warning "DRY RUN MODE - No changes will be made" + print_info "Would push existing commits to: origin/$TARGET_BRANCH" + exit 0 + fi + + # Safety confirmation for push-only (unless forced) + if [ "$FORCE" = false ]; then + echo + read -p "Push existing commits to origin/$TARGET_BRANCH? [y/N] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "Aborted by user" + exit 0 + fi + fi + + # Execute push only + print_info "Pushing existing commits to $TARGET_BRANCH..." + # Check if branch exists on remote to determine if we need -u flag + if git ls-remote --exit-code --heads origin "$TARGET_BRANCH" >/dev/null 2>&1; then + # Branch exists on remote, normal push + if git push origin "$TARGET_BRANCH"; then + print_success "Successfully pushed existing commits 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 + else + # Branch doesn't exist on remote, use -u to set upstream + print_info "Setting upstream branch (first push to origin/$TARGET_BRANCH)" + if git push -u origin "$TARGET_BRANCH"; then + print_success "Successfully pushed existing commits and set upstream to origin/$TARGET_BRANCH" + else + print_error "Failed to push to origin/$TARGET_BRANCH" + exit 1 + fi + fi + exit 0 + fi + + # Show current status and get confirmation for file additions + show_status_and_confirm + + # 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..." + if ! commit_message=$(generate_commit_message); 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" +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/publish.sh b/publish.sh new file mode 100755 index 0000000..661730a --- /dev/null +++ b/publish.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -euo pipefail + +#set projec to the folder this script is in. +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +PROJECT=$(basename "$SCRIPT_DIR") +SOS_DIR="${SCRIPT_DIR}/../sos" + +# run sos to upload sos +"${SOS_DIR}/sos" upload "getbin.xyz" "${SCRIPT_DIR}/${PROJECT}" "${PROJECT}:latest" + +# publish the tool +GETPKG="${SCRIPT_DIR}/../getpkg/output/getpkg" +[ -f "${GETPKG}" ] || die "Failed to find getpkg" +TOOLDIR="${SCRIPT_DIR}/tool" +mkdir -p "${TOOLDIR}" +cp "${SCRIPT_DIR}/${PROJECT}" "${TOOLDIR}/${PROJECT}" +"${GETPKG}" publish "${PROJECT}" "${TOOLDIR}" +rm -rf "${TOOLDIR}"