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 theremotes/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 theirsec1
.
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