Skip to content

Instantly share code, notes, and snippets.

@alvesvaren
Created May 30, 2025 11:06
Show Gist options
  • Select an option

  • Save alvesvaren/28b89dac25628f83e481d022c4916f6c to your computer and use it in GitHub Desktop.

Select an option

Save alvesvaren/28b89dac25628f83e481d022c4916f6c to your computer and use it in GitHub Desktop.

Revisions

  1. alvesvaren created this gist May 30, 2025.
    907 changes: 907 additions & 0 deletions .git-aliases.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,907 @@
    #!/bin/bash
    # Git aliases and functions compatible with Git Bash on Windows

    # --- Helper Functions ---

    # Function to compare version numbers (requires version $1 >= version $2)
    # Usage: is_at_least "2.8" "$git_version"
    is_at_least() {
    local required_version="$1"
    local current_version="$2"
    # Handle empty current_version
    if [[ -z "$current_version" ]]; then
    return 1 # False
    fi
    # Use sort -V for version comparison
    # Check if current_version is >= required_version
    if [[ "$(printf '%s\n' "$required_version" "$current_version" | sort -V | head -n1)" == "$required_version" ]]; then
    return 0 # True (or equal)
    else
    return 1 # False
    fi
    }

    # Get Git version
    git_version_string=$(git version 2>/dev/null)
    if [[ -n "$git_version_string" ]]; then
    # Extract the version number (e.g., 2.34.1 from "git version 2.34.1.windows.1")
    # Using awk for simplicity
    git_version=$(echo "$git_version_string" | awk '{print $3}')
    # Optional: remove suffixes like .windows.1 if needed for comparisons, though sort -V handles it often
    # git_version="${git_version%%.windows.*}"
    else
    git_version="" # Handle case where git isn't found or fails
    # echo "Warning: Could not determine git version." >&2
    fi


    # Get the current branch name reliably
    git_current_branch() {
    local branch_name
    # Try symbolic-ref first, handles branches correctly
    branch_name=$(git symbolic-ref --short HEAD 2>/dev/null)
    local exit_code=$?
    if [[ $exit_code -ne 0 ]]; then
    # Fallback for detached HEAD state: show commit hash
    branch_name=$(git rev-parse --short HEAD 2>/dev/null)
    [[ -z "$branch_name" ]] && return 1 # Failed to get anything
    fi
    echo "$branch_name"
    return 0
    }

    # --- Functions Current ---

    # Back-compatibility wrapper
    function current_branch() {
    git_current_branch
    }

    # Check for develop and similarly named branches
    function git_develop_branch() {
    command git rev-parse --git-dir &>/dev/null || return
    local branch
    for branch in dev devel develop development; do
    if command git show-ref -q --verify refs/heads/"$branch"; then
    echo "$branch"
    return 0
    fi
    done

    echo "develop" # Default fallback name
    return 1 # Indicate default was used / standard names not found
    }

    # Check if main exists and use instead of master
    function git_main_branch() {
    command git rev-parse --git-dir &>/dev/null || return
    local ref
    # Expands correctly in Bash
    for ref in refs/{heads,remotes/{origin,upstream}}/{main,trunk,mainline,default,stable,master}; do
    if command git show-ref -q --verify "$ref"; then
    # Use basename for Bash equivalent of Zsh :t
    echo "$(basename "$ref")"
    return 0
    fi
    done

    # If no main branch was found, fall back to master but return error
    echo "master" # Default fallback name
    return 1 # Indicate default was used / preferred names not found
    }

    function grename() {
    if [[ -z "$1" || -z "$2" ]]; then
    # Use function name directly in Bash
    echo "Usage: grename old_branch new_branch"
    return 1
    fi

    # Rename branch locally
    git branch -m "$1" "$2"
    # Rename branch in origin remote
    # Suppress error if old branch didn't exist remotely
    if git push origin :"${1}" &>/dev/null; then
    git push --set-upstream origin "$2"
    else
    # Optionally inform user, but still try setting upstream for the new branch
    echo "Info: Remote branch '$1' not found or couldn't be deleted." >&2
    git push --set-upstream origin "$2"
    fi
    }

    # --- Functions Work in Progress (WIP) ---

    # Similar to `gunwip` but recursive "Unwips" all recent `--wip--` commits
    function gunwipall() {
    local _commit
    _commit=$(git log --grep='--wip--' --invert-grep --max-count=1 --format=format:%H 2>/dev/null)
    local current_head
    current_head=$(git rev-parse HEAD 2>/dev/null)

    # Check if a non-WIP commit was found and it's different from HEAD
    if [[ -n "$_commit" && "$_commit" != "$current_head" ]]; then
    git reset "$_commit" || return 1
    elif [[ -z "$_commit" ]]; then
    echo "No non-WIP commit found to reset to." >&2
    return 1
    fi
    }

    # Warn if the current branch is a WIP
    function work_in_progress() {
    # Use standard grep and command structure
    if command git -c log.showSignature=false log -n 1 2>/dev/null | grep -q -- '--wip--'; then
    echo "WIP!!"
    fi
    }

    # --- Aliases ---

    alias grt='cd "$(git rev-parse --show-toplevel || echo .)"'

    # Helper function for ggpnp
    _ggpnp_func() {
    if [[ "$#" == 0 ]]; then
    _ggl_func && _ggp_func # Assuming _ggl_func and _ggp_func are defined below
    else
    # Pass arguments individually using "$@"
    _ggl_func "$@" && _ggp_func "$@"
    fi
    }
    alias ggpnp='_ggpnp_func'
    # No direct compdef equivalent in Bash alias files

    alias ggpur='_ggu_func' # Use the helper function defined below
    alias g='git'
    alias ga='git add'
    alias gaa='git add --all'
    alias gapa='git add --patch'
    alias gau='git add --update'
    alias gav='git add --verbose'
    # Ensure deleted files are handled robustly
    alias gwip='git add -A && git commit --no-verify --no-gpg-sign --message "--wip-- [skip ci]"'
    alias gam='git am'
    alias gama='git am --abort'
    alias gamc='git am --continue'
    alias gamscp='git am --show-current-patch'
    alias gams='git am --skip'
    alias gap='git apply'
    alias gapt='git apply --3way'
    alias gbs='git bisect'
    alias gbsb='git bisect bad'
    alias gbsg='git bisect good'
    alias gbsn='git bisect new' # Assuming 'new' is valid; 'start' is common
    alias gbso='git bisect old' # Assuming 'old' is valid; 'bad'/'good' are common
    alias gbsr='git bisect reset'
    alias gbss='git bisect start'
    alias gbl='git blame -w'
    alias gb='git branch'
    alias gba='git branch --all'
    alias gbd='git branch --delete'
    alias gbD='git branch --delete --force'

    # Helper function for gbda
    _gbda_func() {
    local main_branch develop_branch branches_to_delete
    main_branch=$(git_main_branch) || main_branch="master" # Fallback if function fails
    develop_branch=$(git_develop_branch) || develop_branch="develop" # Fallback if function fails

    # Escape branches for the regex
    local safe_main=$(printf '%s\n' "$main_branch" | sed 's/[^^]/[&]/g; s/\^/\\^/g')
    local safe_develop=$(printf '%s\n' "$develop_branch" | sed 's/[^^]/[&]/g; s/\^/\\^/g')

    # Get merged branches excluding main/develop and the current branch (*)
    branches_to_delete=$(git branch --no-color --merged | command grep -vE "^\*|^\s*($safe_main|$safe_develop)\s*$" | sed 's/^\s*//') # Trim whitespace

    if [[ -n "$branches_to_delete" ]]; then
    # Use xargs safely with null delimiter if possible (requires GNU xargs/findutils)
    # Otherwise, process line by line for safety with spaces/special chars
    echo "$branches_to_delete" | while IFS= read -r branch; do
    # Double check it's not main/develop again, just in case
    if [[ "$branch" != "$main_branch" && "$branch" != "$develop_branch" ]]; then
    git branch --delete "$branch"
    fi
    done
    else
    echo "No merged branches (excluding ${main_branch}, ${develop_branch}, and current) to delete."
    fi
    }
    alias gbda='_gbda_func'

    # Helper function for gbds (Delete Squashed)
    _gbds_func() {
    local default_branch main_branch_status
    default_branch=$(git_main_branch)
    main_branch_status=$?
    if [[ $main_branch_status -ne 0 ]]; then
    default_branch=$(git_develop_branch)
    if [[ $? -ne 0 ]]; then
    echo "Warning: Could not determine main or develop branch for comparison. Using 'master' as fallback." >&2
    default_branch="master" # Or choose another default or error out
    fi
    fi

    if [[ -z "$default_branch" ]]; then
    echo "Error: Could not determine a default branch to compare against." >&2
    return 1
    fi

    git for-each-ref refs/heads/ --format='%(refname:short)' | while read branch; do
    # Skip the default branch itself
    if [[ "$branch" == "$default_branch" ]]; then
    continue
    fi

    local merge_base
    merge_base=$(git merge-base "$default_branch" "$branch" 2>/dev/null)
    if [[ -z "$merge_base" ]]; then
    # echo "Info: Cannot find merge base between $default_branch and $branch. Skipping." >&2
    continue
    fi

    # Check if the branch tip is reachable from default_branch (already merged)
    if git merge-base --is-ancestor "$branch" "$default_branch"; then
    # echo "Info: Branch '$branch' seems merged into '$default_branch'. Consider 'gbda'." >&2
    continue # Already merged, gbda handles this
    fi

    # Create a temporary commit to check with git cherry
    # This checks if the *changes* introduced by the branch relative to its merge-base
    # are already present in the default branch (e.g., due to squash merge or cherry-pick)
    local branch_tree branch_commit temp_commit
    branch_commit=$(git rev-parse "$branch" 2>/dev/null)
    branch_tree=$(git rev-parse "$branch^{tree}" 2>/dev/null)

    if [[ -z "$branch_commit" || -z "$branch_tree" ]]; then
    echo "Warning: Could not parse branch '$branch'. Skipping." >&2
    continue
    fi

    # Create a commit object in memory representing the branch's state based on merge-base
    # The message "_" is arbitrary
    temp_commit=$(git commit-tree "$branch_tree" -p "$merge_base" -m "_" 2>/dev/null)

    if [[ -z "$temp_commit" ]]; then
    echo "Warning: Could not create temporary commit for branch '$branch'. Skipping." >&2
    continue
    fi

    # Use git cherry to find commits in temp_commit not equivalent in default_branch
    # If 'git cherry' output starts with '-', it means the commit is equivalent (present)
    # If output starts with '+', it means the commit is not equivalent (not present)
    local cherry_output
    cherry_output=$(git cherry -v "$default_branch" "$temp_commit" 2>/dev/null)

    # Check if cherry output indicates the commit *is* present (starts with '-')
    # We check if the output is *not* empty AND *does not* start with '+'
    # This implies it starts with '-' or is empty (error?), meaning changes are likely present.
    # A safer check might be specifically for '-'.
    if [[ "$cherry_output" == -* ]]; then
    echo "Branch '$branch' changes appear to be present in '$default_branch' (squashed/cherry-picked?). Deleting with -D."
    git branch -D "$branch"
    # else
    # echo "Branch '$branch' has unique changes compared to '$default_branch'."
    fi
    done
    }
    alias gbds='_gbds_func'


    # Use standard quoting for awk
    alias gbgd='LANG=C git branch --no-color -vv | grep ": gone]" | cut -c 3- | awk '\''{print $1}'\'' | xargs -r git branch -d' # Added -r to xargs
    alias gbgD='LANG=C git branch --no-color -vv | grep ": gone]" | cut -c 3- | awk '\''{print $1}'\'' | xargs -r git branch -D' # Added -r to xargs
    alias gbm='git branch --move'
    alias gbnm='git branch --no-merged'
    alias gbr='git branch --remote'

    # Helper for ggsup
    _ggsup_func() {
    local current_b
    current_b=$(git_current_branch)
    if [[ -n "$current_b" ]]; then
    git branch --set-upstream-to="origin/${current_b}"
    else
    echo "Error: Could not determine current branch." >&2
    return 1
    fi
    }
    alias ggsup='_ggsup_func'

    alias gbg='LANG=C git branch -vv | grep ": gone]"' # Shows branches whose upstream is gone
    alias gco='git checkout'
    alias gcor='git checkout --recurse-submodules'
    alias gcb='git checkout -b'
    alias gcB='git checkout -B'

    # Helper for gcd
    _gcd_func() {
    local target_branch
    target_branch=$(git_develop_branch) # Gets the name, status indicates if default was used
    git checkout "$target_branch"
    }
    alias gcd='_gcd_func'

    # Helper for gcm
    _gcm_func() {
    local target_branch
    target_branch=$(git_main_branch) # Gets the name, status indicates if default was used
    git checkout "$target_branch"
    }
    alias gcm='_gcm_func'

    alias gcp='git cherry-pick'
    alias gcpa='git cherry-pick --abort'
    alias gcpc='git cherry-pick --continue'
    alias gclean='git clean --interactive -d'
    alias gcl='git clone --recurse-submodules'
    # Filter options might depend on Git version, but generally compatible
    alias gclf='git clone --recursive --shallow-submodules --filter=blob:none --also-filter-submodules'

    # Helper function for gccd (clone and cd)
    gccd() {
    local last_arg target_dir repo_url clone_dir
    # Store the last argument before git clone runs
    # Bash doesn't have a simple equivalent for Zsh's $_ behavior related to command output directories
    # We'll rely on the standard git clone behavior

    # If the last arg looks like a directory target, save it
    if [[ "$#" -gt 0 && ! "${@: -1}" =~ ^(git@|https?://|ssh://|ftps?://) ]]; then
    clone_dir="${@: -1}"
    fi

    command git clone --recurse-submodules "$@" || return $?

    # Now determine where git cloned it
    if [[ -n "$clone_dir" && -d "$clone_dir" ]]; then
    # If an explicit directory was given and exists now, use it
    target_dir="$clone_dir"
    else
    # Otherwise, infer from the last argument that was likely the URL
    repo_url="${@: -1}" # Simplistic assumption: last arg was URL
    # Derive directory name from URL: basename, remove .git suffix
    target_dir=$(basename "$repo_url" .git)
    fi

    # Check if the determined directory exists and cd into it
    if [[ -d "$target_dir" ]]; then
    cd "$target_dir" || { echo "Error: Failed to cd into '$target_dir'" >&2; return 1; }
    echo "Cloned and changed directory to '$target_dir'"
    else
    echo "Warning: Cloned repository, but could not determine target directory '$target_dir' to cd into." >&2
    # Stay in the current directory
    fi
    }
    # No compdef equivalent

    alias gcam='git commit --all --message'
    alias gcas='git commit --all --signoff'
    alias gcasm='git commit --all --signoff --message'
    alias gcs='git commit --gpg-sign'
    alias gcss='git commit --gpg-sign --signoff'
    alias gcssm='git commit --gpg-sign --signoff --message'
    alias gcmsg='git commit --message'
    alias gcsm='git commit --signoff --message'
    alias gc='git commit --verbose'
    alias gca='git commit --verbose --all'
    alias gca!='git commit --verbose --all --amend'
    alias gcan!='git commit --verbose --all --no-edit --amend'
    alias gcans!='git commit --verbose --all --signoff --no-edit --amend'
    alias gcann!='git commit --verbose --all --date=now --no-edit --amend' # Check if --date=now works as expected
    alias gc!='git commit --verbose --amend'
    alias gcn='git commit --verbose --no-edit' # Assumes you want --no-edit always
    alias gcn!='git commit --verbose --no-edit --amend'
    alias gcf='git config --list'
    alias gdct='git describe --tags $(git rev-list --tags --max-count=1)'
    alias gd='git diff'
    alias gdca='git diff --cached'
    alias gdcw='git diff --cached --word-diff'
    alias gds='git diff --staged' # Same as gdca
    alias gdw='git diff --word-diff'

    # Helper for gdv (diff with view - needs 'view' or adjust editor)
    _gdv_func() {
    # On Windows 'view' might not exist. 'less' is common in Git Bash.
    # Or use default $GIT_EDITOR / $EDITOR / vi
    local editor=${GIT_PAGER:-${GIT_EDITOR:-${EDITOR:-less}}} # Prioritize pager, then editors, fallback less
    git diff -w "$@" | "$editor"
    }
    alias gdv='_gdv_func'
    # No compdef equivalent

    alias gdup='git diff @{upstream}'

    # Helper function for gdnolock (diff exclude lock files)
    _gdnolock_func() {
    # Bash doesn't have the :(exclude) syntax directly in arguments like Zsh
    # Use pathspecs passed to git diff
    git diff "$@" -- . ":(exclude)package-lock.json" ":(exclude)*.lock"
    }
    alias gdnolock='_gdnolock_func'
    # No compdef equivalent

    alias gdt='git diff-tree --no-commit-id --name-only -r'
    alias gf='git fetch'

    # Conditional alias for gfa based on Git version
    if is_at_least "2.8" "$git_version"; then
    alias gfa='git fetch --all --tags --prune --jobs=10'
    else
    alias gfa='git fetch --all --tags --prune'
    fi
    alias gfo='git fetch origin'
    alias gg='git gui citool &' # Use & for background
    alias gga='git gui citool --amend &' # Use & for background
    alias ghh='git help'
    alias glgg='git log --graph'
    alias glgga='git log --graph --decorate --all'
    alias glgm='git log --graph --max-count=10'
    # Use printf for colors in log formats if needed, though Git often handles auto color
    # Assuming terminal supports colors, Git's %C(...) usually works in Git Bash
    alias glods='git log --graph --pretty="%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset" --date=short'
    alias glod='git log --graph --pretty="%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset"'
    alias glola='git log --graph --pretty="%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset" --all'
    alias glols='git log --graph --pretty="%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset" --stat'
    alias glol='git log --graph --pretty="%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset"'
    alias glo='git log --oneline --decorate'
    alias glog='git log --oneline --decorate --graph'
    alias gloga='git log --oneline --decorate --graph --all'

    # Pretty log messages helper function
    _git_log_prettily() {
    if [[ -n "$1" ]]; then
    git log --pretty="$1"
    else
    # Optional: Provide default log or usage message
    git log --oneline --decorate --graph --max-count=20
    # echo "Usage: glp <format_string>" >&2
    # return 1
    fi
    }
    alias glp='_git_log_prettily'
    # No compdef equivalent

    alias glg='git log --stat'
    alias glgp='git log --stat --patch'
    # grep syntax is compatible
    alias gignored='git ls-files -v | grep "^[[:lower:]]"'
    alias gfg='git ls-files | grep'
    alias gm='git merge'
    alias gma='git merge --abort'
    alias gmc='git merge --continue'
    alias gms='git merge --squash'
    alias gmff='git merge --ff-only'

    # Helper for gmom
    _gmom_func() {
    local target_branch
    target_branch=$(git_main_branch) # Gets name, status ignored here
    git merge "origin/${target_branch}"
    }
    alias gmom='_gmom_func'

    # Helper for gmum
    _gmum_func() {
    local target_branch
    target_branch=$(git_main_branch) # Gets name, status ignored here
    git merge "upstream/${target_branch}"
    }
    alias gmum='_gmum_func'

    alias gmtl='git mergetool --no-prompt'
    alias gmtlvim='git mergetool --no-prompt --tool=vimdiff'
    alias gl='git pull'
    alias gpr='git pull --rebase'
    alias gprv='git pull --rebase -v'
    alias gpra='git pull --rebase --autostash'
    alias gprav='git pull --rebase --autostash -v'

    # Helper function for ggu (pull --rebase current or specified)
    _ggu_func() {
    local b
    if [[ "$#" -eq 0 ]]; then
    b=$(git_current_branch)
    if [[ -z "$b" ]]; then echo "Error: No current branch found." >&2; return 1; fi
    command git pull --rebase origin "$b"
    elif [[ "$#" -eq 1 ]]; then
    command git pull --rebase origin "$1"
    else
    echo "Usage: ggu [branch_name]" >&2
    return 1
    fi
    }
    alias ggu='_ggu_func'
    # No compdef equivalent

    # Helper for gprom
    _gprom_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git pull --rebase origin "$target_branch"
    }
    alias gprom='_gprom_func'

    # Helper for gpromi
    _gpromi_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git pull --rebase=interactive origin "$target_branch"
    }
    alias gpromi='_gpromi_func'

    # Helper for gprum
    _gprum_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git pull --rebase upstream "$target_branch"
    }
    alias gprum='_gprum_func'

    # Helper for gprumi
    _gprumi_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git pull --rebase=interactive upstream "$target_branch"
    }
    alias gprumi='_gprumi_func'

    # Helper function for ggpull (pull current branch from origin)
    _ggpull_func() {
    local current_b
    current_b=$(git_current_branch)
    if [[ -n "$current_b" ]]; then
    git pull origin "$current_b"
    else
    echo "Error: Could not determine current branch." >&2
    return 1
    fi
    }
    alias ggpull='_ggpull_func'

    # Helper function for ggl (pull origin current or specified)
    _ggl_func() {
    local b
    if [[ "$#" -eq 0 ]]; then
    b=$(git_current_branch)
    if [[ -z "$b" ]]; then echo "Error: No current branch found." >&2; return 1; fi
    command git pull origin "$b"
    else
    # Pass all arguments using "$@" (handles spaces in names if they occur)
    command git pull origin "$@"
    fi
    }
    alias ggl='_ggl_func'
    # No compdef equivalent

    # Helper for gluc
    _gluc_func() {
    local current_b
    current_b=$(git_current_branch)
    if [[ -n "$current_b" ]]; then
    git pull upstream "$current_b"
    else
    echo "Error: Could not determine current branch." >&2
    return 1
    fi
    }
    alias gluc='_gluc_func'

    # Helper for glum
    _glum_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git pull upstream "$target_branch"
    }
    alias glum='_glum_func'

    alias gp='git push'
    alias gpd='git push --dry-run'

    # Helper function for ggf (force push origin current or specified)
    _ggf_func() {
    local b
    if [[ "$#" -eq 0 ]]; then
    b=$(git_current_branch)
    if [[ -z "$b" ]]; then echo "Error: No current branch found." >&2; return 1; fi
    command git push --force origin "$b"
    elif [[ "$#" -eq 1 ]]; then
    command git push --force origin "$1"
    else
    echo "Usage: ggf [branch_name]" >&2
    return 1
    fi
    }
    alias ggf='_ggf_func'
    # No compdef equivalent

    alias gpf!='git push --force'

    # Conditional alias for gpf (force with lease)
    if is_at_least "2.30" "$git_version"; then
    alias gpf='git push --force-with-lease --force-if-includes'
    else
    alias gpf='git push --force-with-lease'
    fi

    # Helper function for ggfl (force-with-lease push origin current or specified)
    _ggfl_func() {
    local b push_command
    # Determine the correct force-with-lease command based on version
    if is_at_least "2.30" "$git_version"; then
    push_command=(git push --force-with-lease --force-if-includes origin)
    else
    push_command=(git push --force-with-lease origin)
    fi

    if [[ "$#" -eq 0 ]]; then
    b=$(git_current_branch)
    if [[ -z "$b" ]]; then echo "Error: No current branch found." >&2; return 1; fi
    "${push_command[@]}" "$b"
    elif [[ "$#" -eq 1 ]]; then
    "${push_command[@]}" "$1"
    else
    echo "Usage: ggfl [branch_name]" >&2
    return 1
    fi
    }
    alias ggfl='_ggfl_func'
    # No compdef equivalent


    # Helper function for gpsup (push set upstream)
    _gpsup_func() {
    local current_b
    current_b=$(git_current_branch)
    if [[ -n "$current_b" ]]; then
    git push --set-upstream origin "$current_b"
    else
    echo "Error: Could not determine current branch." >&2
    return 1
    fi
    }
    alias gpsup='_gpsup_func'

    # Helper function for gpsupf (push set upstream force-with-lease)
    _gpsupf_func() {
    local current_b push_command base_command
    current_b=$(git_current_branch)
    if [[ -z "$current_b" ]]; then
    echo "Error: Could not determine current branch." >&2
    return 1
    fi

    base_command=(git push --set-upstream origin "$current_b")
    if is_at_least "2.30" "$git_version"; then
    push_command=("${base_command[@]}" --force-with-lease --force-if-includes)
    else
    push_command=("${base_command[@]}" --force-with-lease)
    fi
    "${push_command[@]}"
    }
    alias gpsupf='_gpsupf_func'


    alias gpv='git push --verbose'
    alias gpoat='git push origin --all && git push origin --tags'
    alias gpod='git push origin --delete'

    # Helper function for ggpush (push current branch to origin)
    _ggpush_func() {
    local current_b
    current_b=$(git_current_branch)
    if [[ -n "$current_b" ]]; then
    git push origin "$current_b"
    else
    echo "Error: Could not determine current branch." >&2
    return 1
    fi
    }
    alias ggpush='_ggpush_func'

    # Helper function for ggp (push origin current or specified)
    _ggp_func() {
    local b
    if [[ "$#" -eq 0 ]]; then
    b=$(git_current_branch)
    if [[ -z "$b" ]]; then echo "Error: No current branch found." >&2; return 1; fi
    command git push origin "$b"
    else
    # Pass all arguments using "$@"
    command git push origin "$@"
    fi
    }
    alias ggp='_ggp_func'
    # No compdef equivalent

    alias gpu='git push upstream'
    alias grb='git rebase'
    alias grba='git rebase --abort'
    alias grbc='git rebase --continue'
    alias grbi='git rebase --interactive'
    alias grbo='git rebase --onto'
    alias grbs='git rebase --skip'

    # Helper for grbd
    _grbd_func() {
    local target_branch
    target_branch=$(git_develop_branch)
    git rebase "$target_branch"
    }
    alias grbd='_grbd_func'

    # Helper for grbm
    _grbm_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git rebase "$target_branch"
    }
    alias grbm='_grbm_func'

    # Helper for grbom
    _grbom_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git rebase "origin/${target_branch}"
    }
    alias grbom='_grbom_func'

    # Helper for grbum
    _grbum_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git rebase "upstream/${target_branch}"
    }
    alias grbum='_grbum_func'

    alias grf='git reflog'
    alias gr='git remote'
    alias grv='git remote --verbose'
    alias gra='git remote add'
    alias grrm='git remote remove'
    alias grmv='git remote rename'
    alias grset='git remote set-url'
    alias grup='git remote update'
    alias grh='git reset'
    alias gru='git reset --' # Use '--' to separate paths from revisions if needed
    alias grhh='git reset --hard'
    alias grhk='git reset --keep'
    alias grhs='git reset --soft'
    alias gpristine='git reset --hard && git clean --force -dfx'
    alias gwipe='git reset --hard && git clean --force -df' # Slightly safer than -dfx

    # Helper for groh
    _groh_func() {
    local current_b
    current_b=$(git_current_branch)
    if [[ -n "$current_b" ]]; then
    git reset "origin/${current_b}" --hard
    else
    echo "Error: Could not determine current branch." >&2
    return 1
    fi
    }
    alias groh='_groh_func'

    alias grs='git restore'
    alias grss='git restore --source'
    alias grst='git restore --staged'
    alias gunwip='last_msg=$(git log -1 --pretty=%s); if [[ "$last_msg" == *"--wip--"* ]]; then git reset HEAD~1; else echo "Last commit is not a WIP commit."; fi'
    alias grev='git revert'
    alias greva='git revert --abort'
    alias grevc='git revert --continue'
    alias grm='git rm'
    alias grmc='git rm --cached'
    alias gcount='git shortlog --summary --numbered'
    alias gsh='git show'
    alias gsps='git show --pretty=short --show-signature'
    alias gstall='git stash --all'
    alias gstaa='git stash apply'
    alias gstc='git stash clear'
    alias gstd='git stash drop'
    alias gstl='git stash list'
    alias gstp='git stash pop'

    # Conditional alias for gsta (stash push/save)
    if is_at_least "2.13" "$git_version"; then
    alias gsta='git stash push'
    else
    alias gsta='git stash save'
    fi

    alias gsts='git stash show --patch'
    alias gst='git status'
    alias gss='git status --short'
    alias gsb='git status --short --branch'
    alias gsi='git submodule init'
    alias gsu='git submodule update'
    alias gsd='git svn dcommit'

    # Helper for git-svn-dcommit-push
    _git_svn_dcommit_push_func() {
    local target_branch
    target_branch=$(git_main_branch)
    # Assuming 'github' is the correct remote name for the Git repo mirror
    git svn dcommit && git push github "${target_branch}:svntrunk"
    }
    alias git-svn-dcommit-push='_git_svn_dcommit_push_func'

    alias gsr='git svn rebase'
    alias gsw='git switch'
    alias gswc='git switch --create'

    # Helper for gswd
    _gswd_func() {
    local target_branch
    target_branch=$(git_develop_branch)
    git switch "$target_branch"
    }
    alias gswd='_gswd_func'

    # Helper for gswm
    _gswm_func() {
    local target_branch
    target_branch=$(git_main_branch)
    git switch "$target_branch"
    }
    alias gswm='_gswm_func'

    alias gta='git tag --annotate'
    alias gts='git tag --sign'
    alias gtv='git tag | sort -V' # Assumes standard sort, Git Bash usually has GNU sort
    alias gignore='git update-index --assume-unchanged'
    alias gunignore='git update-index --no-assume-unchanged'
    alias gwch='git whatchanged -p --abbrev-commit --pretty=medium'
    alias gwt='git worktree'
    alias gwta='git worktree add'
    alias gwtls='git worktree list'
    alias gwtmv='git worktree move'
    alias gwtrm='git worktree remove'
    alias gstu='gsta --include-untracked' # Relies on gsta alias defined above

    # Helper function for gtl (list tags matching prefix)
    _gtl_func() {
    # Bash doesn't need noglob here. Ensure $1 is quoted.
    git tag --sort=-v:refname -n --list "${1}*"
    }
    alias gtl='_gtl_func'

    # Use `command` prefix and `&` for background in Bash
    alias gk='command gitk --all --branches &'
    alias gke='command gitk --all $(git log --walk-reflogs --pretty=%h) &'


    # --- Deprecated Alias Handling ---

    # Define functions for old aliases that show a warning and call the new one
    # ANSI color codes: Yellow=\033[0;33m, Red=\033[0;31m, Green=\033[0;32m, Reset=\033[0m
    deprecated_map=(
    "gup:gpr"
    "gupv:gprv"
    "gupa:gpra"
    "gupav:gprav"
    "gupom:_gprom_func" # Call helper func directly if new alias uses one
    "gupomi:_gpromi_func" # Call helper func directly
    )

    item="" old_alias="" new_alias_cmd=""
    for item in "${deprecated_map[@]}"; do
    old_alias="${item%%:*}" # Part before :
    new_alias_cmd="${item#*:}" # Part after :

    # Use eval to dynamically create a function named $old_alias
    # This function prints a warning and executes the new command, passing arguments
    eval "
    ${old_alias}() {
    printf \"\\033[0;33m[Git Alias] Warning: '%s' is deprecated, using '%s' instead.\\033[0m\\n\" \"${old_alias}\" \"${new_alias_cmd}\" >&2
    \"${new_alias_cmd}\" \"\$@\" # Execute the new command/function with original arguments
    }
    "
    done
    unset item old_alias new_alias_cmd deprecated_map # Clean up loop variables

    # --- Cleanup ---
    unset git_version_string git_version # Clean up global version variable

    # --- End of Script ---
    # To use these aliases, source this file in your ~/.bashrc or ~/.bash_profile:
    # source /path/to/this/script.sh