Home > Enterprise >  How to delete a remote branch locally after deleting the branch in GitHub?
How to delete a remote branch locally after deleting the branch in GitHub?

Time:10-31

In my GitHub, I have successfully merged my pull request and deleted the branch sec1. Then, I delete my branch locally. However, when I type git branch -a, I still can see the remotes/origin/sec1 branch, like shown below

* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main
  remotes/origin/sec1

Then, I try git push origin --delete sec1 but it gives me this error:

error: failed to push some refs to https:/.../

Anyone can help with this?

CodePudding user response:

TL;DR: I recommend git fetch --prune, but via a different route (set fetch.prune to true and then just run git fetch).

Longer

In my GitHub, I have successfully merged my pull request and deleted the branch sec1.

Your subsequent git push -d command would be correct, and would work, if you had not already done the deletion. The branch in the GitHub repository is already gone, and the attempt to delete it again fails because there's nothing to to do!

Then, I delete my branch locally.

Note that their sec1 is your origin/sec1; your own sec1 is your sec1. So you've now deleted their sec1 and your sec1.

However, when I type git branch -a, I still can see the remotes/origin/sec1 branch

That's not actually a branch. That's your Git's memory of their Git's branch name. I prefer to call this a remote-tracking name.

There are two ways to get your Git to delete this:

  • Use git branch -r -d: the -d is the delete flag, and the -r flag tells your Git that you're interested not in branch names, but rather in the remote-tracking names. (The -a flag you're using now tells your Git that you are interested in all names, i.e., both branch names and remote-tracking names. You can only use this with the listing options, while you can use the -r option for listing or for deleting.)

  • Or, get your Git to scan their (GitHub's) branches again and see that their sec1 is gone and therefore your Git should delete its memory of their sec1.

Details

The first one is pretty straightforward: the command

git branch -d -r origin/sec1

directs your Git to delete the remote-tracking name origin/sec1. It goes away and you're done. But it's annoying to have to do this a lot.

Note that you can delete any remote-tracking name at any time. When you run:

git fetch origin

or just:

git fetch

your Git—by "your Git" I mean your Git software, working with your repository, locally on your laptop—calls up their Git (the repository and Git software over on GitHub where you cloned from). Their Git lists out all their branch names and the commit hash IDs that go with those branch names.

Your Git now decides which of those branch names are "interesting" to you. The default, for a normal clone, is "all of them", but you can make a so-called single-branch clone where your Git throws away all but one name. Your Git then asks their Git for any commits they have that you don't, because the real purpose of Git is to store commits. So they now package up any commits they have, that you don't, that you need to go along with their branch names, and send those over. That's when you see stuff like:

remote: Counting objects: 100% ...

and:

Receiving objects: ...

and so on: at this point, your Git is getting their commits to add to your repository.

Once your Git has safely saved away all their commits and other supporting objects, your Git now updates your Git's memory of their Git's branch names. These are your remote-tracking names. Your Git simply takes each of their branch names and sticks origin/ in front of them.

(The origin/ part here is from the name origin, which is what we used when we ran git fetch origin. This name is a remote. You can have more than one, but git clone sets up the first standard remote using the name origin, and in many cases that's all we ever need. So origin is the one you see all the time. The remote's #1 purpose is to remember the URL: the ssh://[email protected]/you/repo.git or whatever, so that you don't have to retype it every time.)

Remote-tracking names are forever, or not

For some reason—probably, "by mistake", though only those who invented these things back in 2005 or so1 could say for sure—whoever invented remotes and remote-tracking names decided that the way they would work by default is that your Git will have their Git list out their branch names, and then after your Git gets all their commits, your Git will create or update a remote-tracking name for each branch.

Well, that's fine for a while. But their Git—in this case, the one over on GitHub—sometimes has branches deleted. If your Git just adds remote-tracking names, and never deletes them, then at some point you run:

git fetch origin

and acquire:

origin/hoopy-frood
origin/zaphod

Later, one or both of these is gone, but your Git never deletes either one. So you—or your Git—will hang on to their branches forever, even though they don't.

You can of course delete them one by one, but wouldn't it make more sense for your Git to notice that, gosh, they used to have a hoopy-frood and a zaphod, but they don't any more? Your Git could automatically delete your origin/hoopy-frood and origin/zaphod at this point.

Well, someone finally got around to putting that into Git, some time after the invention of remote-tracking names. They added the --prune option:

git fetch origin --prune

means call up the Git at origin, list out their branch names, get their commits and create or update my remote-tracking names, and oh by the way delete the cruft too please. This ought to be the default, but it was too late to make it the default.

Fortunately, you can make it your default. Set the fetch.prune option to true in your Git configuration, and it becomes your default. Use:

git config --global fetch.prune true

to set this in your per-user configuration, so that it applies to all your repositories. Then:

git fetch origin

will reach out to origin, list out their branch names, get their commits, and clean up too. Running git fetch without specifying origin in particular will reach out to the appropriate remote, whatever that is,2 and do the same sort of thing.


1Very early Git didn't have these. You ran git fetch url every time, and then carefully picked stuff out from the mess it left behind. Both the typing-in of URLs, and the picking-out of stuff, got boring and repetitive, and there were several competing ways to make this easier. The method of using remotes and remote-tracking names won the popularity contest, but you can still see pieces of the various other methods if you read the git fetch documentation carefully.

2If you have more than one remote, sometimes you'll want git fetch to fetch from one, and sometimes you will want it to fetch from another. Git figures out which one to use based on the upstream setting of the current branch, which is usually what you want.

If you want git fetch to fetch from all remotes, you can use git fetch --all. However, it's usually better, if you want this sort of thing, to switch to using git remote update, which has fancier control. It defaults to updating all remotes but lets you do all kinds of clever stuff. See enter image description here

  • Related