8

I've been trying to find a way to delete local git branches with no upstream branch, but none of the answers in the linked question worked for me. It seems that they all rely on git branch -vv outputting gone] for branches with no remote, but it does not do so for me, and I can't figure out why.

For example, after running get fetch --prune, git branch -vv shows the branch rsg/revert with no [gone] tag.

  rsg/revert                       af2c4ac81e Remove temporary debug logging

However, there is no corresponding origin/rsg/revert branch (I already deleted it on Github). Does anyone know why this isn't working?

Edit: I'm using git 2.15.0 on a Mac. I've never had issues with it before.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • How does local `git` know that the remote is gone? I would try `git fetch --all` first. – 9000 Oct 31 '19 at 21:16
  • I tried `git fetch --all` but there was no change. I already fetched and pruned everything before running `git branch -vv` anyway, just to see if it would help. – Antimony Oct 31 '19 at 21:17
  • I don't see anywhere in [this answer](https://stackoverflow.com/a/17029936/1440565) that refers to the string "[gone]". Did you try the command they give? – Code-Apprentice Oct 31 '19 at 21:21
  • You definitely need to `git fetch -p` (or `--prune`) first. If your Git is a particularly old vintage, `fetch -p` and `git remote prune` differ in behavior and one of the two works better, so if you have, e.g., Git 1.7 or something, try `git remote prune` as well. – torek Oct 31 '19 at 21:22
  • I have git 2.15.0, but I tried running `git remote prune origin` just in case. No changes. – Antimony Oct 31 '19 at 21:34
  • Do these branches have an upstream configured? If not, they're considered local. To know they're eligible for removal they must track a branch that's no longer on the remote... – jessehouwing Oct 31 '19 at 21:42
  • I pushed the branches to Github, then merged them and deleted the branch on Github. Surely that should count? – Antimony Oct 31 '19 at 21:51

3 Answers3

6

Your Git is new enough and you've run git fetch --prune, so there's only one conclusion left: rsg/revert has the wrong upstream or no upstream at all.

To see the upstream setting—or get an error if there is none—use git rev-parse like this (note that some shells may require quoting the curly braces):

git rev-parse --abbrev-ref rsg/revert@{u}

For example:

$ git rev-parse --abbrev-ref master@{u}
origin/master
$ git rev-parse --abbrev-ref dev@{u}
fatal: no upstream configured for branch 'dev'

The upstream of my master here is origin/master, but there is no upstream set for my dev.

The git branch -vv output will include the word gone in square brackets if and only if:

  • the branch has an upstream set, and
  • the upstream that is set for that branch no longer refers to a valid name.

So if I somehow delete origin/master from my own Git repository (which I can do with various Git maintenance commands, or with git branch -d -r origin/master) I will get:

$ git branch -d -r origin/master
Deleted remote-tracking branch origin/master (was 08da6496b6).
$ git branch -vv
  dev             9c9b961d7e The sixth batch
* master          08da6496b6 [origin/master: gone] Eighth batch

(I then ran git fetch to pick up new commits and re-create my origin/master, which is now f21f8f5d35 instead of 08da6496b6.)

Given that you had GitHub delete rsg/revert and then ran a git fetch -p that deleted origin/rsg/revert, your rsg/revert branch must something else, or nothing, as its upstream.

torek
  • 448,244
  • 59
  • 642
  • 775
  • I get `fatal: no upstream configured for branch 'rsg/revert'`. Any idea how to fix the upstream thing? The ultimate goal is to be able to use the commands from the linked question to delete all my obsolete local branches. – Antimony Oct 31 '19 at 22:13
  • You can manually set the upstream of any branch at any time using `git branch --set-upstream-to` (the `-to` part of this flag at the end is because there's a `--set-upstream` option that should not be used any more, it has confusing argument order). You can remove it at any time with `git branch --unset-upstream`. You can have `git push` set the upstream of a branch automatically after a successful `git push` using `git push --set-upstream`, which can be abbreviated as `git push -u`. **But**... – torek Oct 31 '19 at 22:18
  • 1
    In this case, there is no point. You have already gone through all this effort and discovered that you would like to delete `rsg/revert`, so you might as well just delete it directly. You'd be doing all this upstream-setting just to be forced to delete the branch again on GitHub (if you use `git push -u`) and then run your script to search for `gone` and delete the local branch name. And, if `origin/rsg/revert` does not exist in your repository, `git branch --set-upstream-to` won't let you use it, because it does not exist, so you'll be forced to create it on GitHub first, etc. – torek Oct 31 '19 at 22:19
  • In my case I just needed to fetch --prune and things got updated nicely. – Mike Cheel Feb 01 '23 at 19:51
2

In case anyone here needs it, the solution I used was a combination of several others answers found on SO.

For the branch that DO have an upstream, the question linked in current question works perfectly.

As for the branch that LACK such upstream, I went with a variation of this answer.

Here is the modified code snippet which I used :

#! /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) || true
    # 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 modification that was done occurs in the first ||, when we get the remote. In case no remote is found, the linked answer leaves the branch AS IS. That's exactly the case we're interested in, we want to SUPPRESS thewse branch. Therefore, we output true, so the line doesn't end up in an exit code.

Be warned that this solution will suppress ANY branch on local that isn't linked to a remote (this includes branches you started but didn't push yet), so use accordingly, and don't hesitate to run first without the -f option.

Hoping this helps someone.

Jonathan Simonney
  • 585
  • 1
  • 11
  • 25
0

As mentioned in other answers, the word gone does not show up if there's no upstream set for the branches.

I fixed this by detecting ] instead of gone.

NOTE: This will also delete branches whose commit messages have ]. So make sure none of the branches you want to keep have it.

git branch -vv | grep -v ']'|  grep -v "\*" | awk '{ print $1; }' | xargs git branch -d

Change -d in the command to -D if you want to delete unmerged branches too:

git branch -vv | grep -v ']'|  grep -v "\*" | awk '{ print $1; }' | xargs git branch -D
Anutrix
  • 145
  • 1
  • 9