'Git command to remove local branches that have a deleted upstream?

There are lots of similar questions to this (e.g. this one or this one) but the accepted answers do not seem to answer my exact question.

Branch1 is not merged into master. Machine A and Machine B both point to the same remote. Branch1 exists on that remote.

On Machine A I delete Branch1 locally and push the deletion to the remote so that Branch1 and remotes/origin/Branch1 are deleted from Machine A and Branch1 is deleted from the remote.

On Machine B I run

  • git checkout Branch1 and then
  • git checkout Branch2 and then
  • git fetch --prune

That git fetch --prune removes remotes/origin/Branch1 from my local repository on Machine B. If I then go back to Branch1 by running git checkout Branch1 I get the error message

Switched to branch 'Branch1'
Your branch is based on 'origin/Branch1', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)

Is there a git command to remove local branches that have a deleted upstream?

git


Solution 1:[1]

Whymarrh's comment is correct: there's no built-in command to do this.

There is a good reason there is no such built-in, as well. Just because the upstream remote-tracking branch has been pruned doesn't mean you're necessarily ready to throw out your own work. In your example, perhaps before git fetch --prune deleted origin/Branch1, just maybe, the commit graph picture looked like this:

...--o--o--o     <-- origin/Branch1
            \
             o   <-- Branch1

where you made one good commit on Branch1 and were ready to git push it but got interrupted last week. Now that origin/Branch1 is gone, perhaps you'd like to copy that good commit somewhere else. But now that origin/Branch1 is gone there's no reason to keep the kink in the graph, and all Git can see is this:

...--o--o--o--o   <-- Branch1

It knows that your Branch1 has a configured upstream, but it doesn't know which commit that configured upstream pointed to, back when it existed.

Nonetheless, if you're quite certain you want to remove such branches, you can do it from a short script:

#! /bin/sh
#
# rm-if-gone: remove branches that have a configured upstream
# where the configured upstream no longer exists.  Run with
# -f to make it work, otherwise it just prints what it would
# remove.
force=false
case "$1" in
-f) force=true;;
esac

for branch in $(git for-each-ref --format='%(refname:short)' refs/heads); do
    # find configured upstream, if any
    remote=$(git config --get branch.$branch.remote) || continue
    # if tracking local branch, skip
    if [ "$remote" = . ]; then continue; fi
    # if the upstream commit resolves, skip
    ucommit=$(git rev-parse "${branch}@{u}" 2>/dev/null) && continue
    # upstream is invalid - remove local branch, or print removal
    $force && git branch -D $branch || echo "git branch -D $branch"
done

(the above is untested; also it won't, because you can't, delete the current branch, so you need to be sure you're on a branch you don't intend to delete).

Solution 2:[2]

There is no built-in way to do this, but it is possible with the following code:

git fetch -p && \ 
git for-each-ref --format '%(refname:short) %(upstream:track)' | \ 
  awk '$2 == \"[gone]\" {print $1}' | \
  xargs -r git branch -D

This command consists of several parts:

  1. git fetch -p: ensure that the remote branches are up-to-date
  2. git for-each-ref --format '%(refname:short) %(upstream:track)': this returns the git branches in a format of our choosing, where we have both the local branch name as well as the upstream branch (which will be "[gone]" for branches where the remote was deleted)
    1. awk '$2 == \"[gone]\" {print $1}': filter the branches which remote status is "[gone]" and return the local branch name
    2. xargs -r git branch -D delete the local branch

You can even add the above command as a git alias by running the following command:

git config --global alias.gone "! git fetch -p && git for-each-ref --format '%(refname:short) %(upstream:track)' | awk '\$2 == \"[gone]\" {print \$1}' | xargs -r git branch -D"

Once this command has finished, you can then do git gone and it will remove the local branches which remote branch has been deleted.

You can read the full explanation in this blog post

Solution 3:[3]

This command has worked for me to remove the 'foo' branch that was deleted upstream.

git branch -d foo

You must be on the main branch locally or you will get this: error: Cannot delete branch 'foo' checked out at...

Solution 4:[4]

git remote prune origin --dry-run

Remove the --dry-run to actually perform the prune. From the docs:

Deletes all stale remote-tracking branches under name. These stale branches have already been removed from the remote repository referenced by name, but are still locally available in "remotes/name".

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2 Erik Schierboom
Solution 3
Solution 4 PatrickSteele