Posted on ::

A useful function for working with git is gbc, standing for Git Branch Clean.

It does the following

  1. Fetch remote-tracking branches and prune, so that branches deleted on the remote are deleted locally
  2. Get the list of local branches that are tracking remote branches and where the remote branch is "gone"
  3. Loop through the list and get the user to confirm deletion for each

This can be implemented as a shell function. Here are some implementations

Fish

Place the below in ~/.config/fish/functions/gbc.fish

function gbc
    git fetch -p && for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}')
        while read --nchars 1 -l response --prompt-str="delete $branch (y/n)"
            or return 1 # if read was aborted with ctrl-c/ctrl-d
            switch $response
                case y Y
                    echo Okay
                    set to_delete true
                    break
                case n N
                    echo Not deleting
                    set to_delete false
                    break
                case '*'
                    # we go through the while loop and ask again
                    echo Not valid input
                    continue
            end
        end
        if $to_delete
            git branch -D $branch

        end
    end
end

PowerShell

Add this to your profile file, which you can open with code $profile. I got Claude Chat to write this, but I needed to fix a bug in the prompt loop.

function gbc {
    # Fetch and prune remote-tracking branches
    git fetch -p

    # Get all local branches with gone remote tracking branches
    $branches = git for-each-ref --format '%(refname) %(upstream:track)' refs/heads |
        Where-Object { $_ -match '\[gone\]' } |
        ForEach-Object {
            $_ -replace '^refs/heads/(\S+).*', '$1'
        }

    foreach ($branch in $branches) {
        $to_delete = $false
        $need_response = $true

        while ($need_response) {
            $response = Read-Host "delete $branch (y/n)"

            switch ($response) {
                { $_ -in 'y', 'Y' } {
                    Write-Host "Okay"
                    $to_delete = $true
                    $need_response = $false
                }
                { $_ -in 'n', 'N' } {
                    Write-Host "Not deleting"
                    $to_delete = $false
                    $need_response = $false
                }
                default {
                    Write-Host "Not valid input"
                    # Continue the while loop
                }
            }
        }

        if ($to_delete) {
            git branch -D $branch
        }
    }
}

Bash/ZSH etc

This was also written by Claude, and it works as is. You can add it to ~/.bashrc or ~/.zshrc.

gbc() {
    # Fetch and prune remote-tracking branches
    git fetch -p

    # Get all local branches with gone remote tracking branches
    local branches=$(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}')

    # Process each branch
    for branch in $branches; do
        local to_delete=false

        while true; do
            # Read user input
            read -n 1 -p "delete $branch (y/n) " response
            echo  # Add newline after response

            # Check if read was aborted (Ctrl+D returns exit code 1)
            if [ $? -ne 0 ]; then
                echo
                return 1
            fi

            case "$response" in
                y|Y)
                    echo "Okay"
                    to_delete=true
                    break
                    ;;
                n|N)
                    echo "Not deleting"
                    to_delete=false
                    break
                    ;;
                *)
                    echo "Not valid input"
                    # Continue the while loop
                    ;;
            esac
        done

        if [ "$to_delete" = true ]; then
            git branch -D "$branch"
        fi
    done
}