'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 thengit checkout Branch2
and thengit 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?
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:
git fetch -p
: ensure that the remote branches are up-to-dategit 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)awk '$2 == \"[gone]\" {print $1}'
: filter the branches which remote status is "[gone]" and return the local branch namexargs -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 byname
, 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 |