I have fetched a few remote branches e.g. git fetch jason sprint-36
. I have attached a screenshot (reds are remotes):
How would I stop tracking/delete a remote from my list? For example 4 months ago I did git fetch ken retain-cycle-fix
and I will never fetch or look at this branch again, how do I remove it?
Use the git remote rm command to remove a remote URL from your repository. The git remote rm command takes one argument: A remote name, for example, destination.
If it's deleted on the remote as well, you can simply use
git fetch --prune
and it will simply prune all the branches that are not in the remotes anymore.
On the other hand if it's not deleted on the remote repository and you won't ever fetch again, you can simply use
git branch -rd <branch_name>
To remove a remote-tracking name like remotes/jason/sprint-36
or remotes/ken/retain-cycle-fix
, you can use git branch -r -d
:
$ git branch -r -d remotes/jason/sprint-36
for instance. However, there's rarely any reason to do that, because if you run git fetch jason
you will immediately re-acquire all your remotes/jason/
remote-tracking names.
To remove an entire remote, along with all of its remote-tracking names, use git remote remove
or git remote rm
(two names for the same sub-command).
There are several things worth noting here, none of which are direct answers to the question but are useful for completeness:
First, Git calls these things remote-tracking branch names, but they are not actually branches, because a branch—or at least, a branch name—is a name you can give to git checkout
that results in attaching HEAD
to that name.
That is, after git checkout master
or, in your case, git checkout sprint-41
, you will be "on" that branch. The git status
command will say on branch branch
, and new commits you make will automatically change that branch name so that it points to the new commit just made. None of this holds for these remote-tracking names, which is why I have taken to calling them "remote-tracking names", dropping the word branch entirely.
They have full-name forms. The full name of remotes/jason/sprint-36
is really refs/remotes/jason/sprint-36
. The full name can be abbreviated thanks to the six-step name-resolution process described in the gitrevisions documentation: jason/sprint-36
usually works, and if it doesn't, remotes/jason/sprint-36
usually works, but if neither of these works, the full-name form always works.
For whatever reason, git branch -r
lists the shortened version without remotes/
while git branch -a
lists the shortened version with remotes/
.
The point of a remote-tracking name is to, well, keep track of a name—specifically, a branch name—on a remote. So git fetch
updates these remote-tracking names. At least, it does so since Git version 1.8.4.
Like all references, a remote-tracking name simply records the hash ID of one single commit. If that commit is reachable from any other name, the remote-tracking name is merely pointing to that particular commit. If the commit is not reachable from any other name, the remote-tracking name is keeping that commit, and its predecessors (parents, grandparents, and so on backwards through history) alive in your repository. (See the link to http://think-like-a-git.net/ for the definition of reachability.)
What this means is that deleting the remote-tracking name will not save any space unless that name is the only way to reach some commits that are taking up a lot of space in your repository. If the remote-tracking name points to a commit that is retained by other commits and/or names, you definitely won't save anything by deleting it, other than some screen space in git branch
output and similar.1
When you run git fetch remote
, with no additional arguments, Git will fetch and update the references listed in the remote.remote.fetch
settings (use git config --get-all remote.remote.fetch
to view these). When you run git fetch remote refspec1 [refspec2 [...]]
, git fetch
will only update based on the given refspec
arguments.
1Technically, you might save one disk block or so if the remote-tracking name is currently unpacked. However, most remote-tracking names are mostly packed (stored in .git/packed-refs
, together with many other names).
--prune
or fetch.prune
You might regularly run git fetch
or git fetch origin
to update all your remote-tracking names for origin
. (I do this, for instance. It's very safe to do it pretty much any time.) When you do, you acquire a lot of remote-tracking names.
Eventually, some of those names might disappear from origin
. They won't automatically disappear from your own remote-tracking names, though. To make them go away automatically, use git fetch --prune origin
, or configure fetch.prune
to true
, which manes git fetch
default to using --prune
. Replace origin
here with any remote, such as jason
or ken
, to update everything with pruning. (But if you don't like to update everything, don't do this at all—this is the "all in one" setting, which creates or updates all the remote-tracking names for that remote, while removing any dead ones.)
git fetch
es you're runningIn one of your examples above, you ran:
git fetch jason sprint-36
This uses sprint-36
as a refspec. A refspec consists, generally, of two names separated by a colon, e.g., refs/heads/master:refs/remotes/origin/master
. Omitting the colon and the second name does something special—and different in git fetch
vs git push
. For git push
, omitting the colon means use the same name on both sides, but for git fetch
, it means don't use any name on my side.
This is where things get weird, and is why I had to mention Git pre-1.8.4 and 1.8.4-and-later, in one of those bullet points. There's a historic oddity here.
Every git fetch
writes information about what names were fetched, and the corresponding Git object hash IDs, to a special file in .git
named FETCH_HEAD
. This file is almost the same as a reference, but not quite, because it can and often does contain multiple lines, with more than just a hash ID on each line. Essentially, git fetch
starts with the output from git ls-remote
(try this on your remotes—it just prints things to the terminal window), figures out what to fetch, and then saves something very similar in .git/FETCH_HEAD
.
In the bad old days, when Git was particularly hard to use, that was the only place git fetch
saved its information. You had to immediately fish out anything useful and save it yourself. The git pull
script did this for you. That still works, because like Intel, the Git guys like to keep the "backwards" in "backwards-compatible". 😀
As of Git 1.8.4, this all changed: Git learned to use the fetch
setting for that remote to update the appropriate remote-tracking branches.
Meanwhile—true then and still true now—if you use a two-name form, e.g.:
git fetch jason sprint-36:rhs
git fetch
will attempt to write what it fetched using the left hand side name, sprint-36
(matched against the list that spills out of git ls-remote jason
so that it becomes refs/heads/sprint-36
) to a branch or tag or whatever name in your own repository. Here, I used rhs
as the right-hand side name. This name is not fully qualified (does not start with refs/heads/
for instance) and probably does not currently exist as a name in your repository, so Git would use the refs/heads/
cue from your jason
remote to create a new branch, refs/heads/rhs
, in your own repository.
As long as your Git is 1.8.4 or newer, it will also opportunistically create or update refs/remotes/jason/sprint-36
at this time.
Hence, fetching with a refspec that includes a colon and a right-hand-side name may create or update two references in your repository. Typically, one would be a branch name like refs/heads/rhs
or refs/heads/sprint-36
. The other would be the remote-tracking name, e.g., refs/remotes/jason/sprint-36
, managed via the remote.jason.fetch
setting in your .git/config
.
You can alter the remote.jason.fetch
setting. If you do, that changes the action of git fetch jason
: with no refspecs, Git obeys the fetch setting. It also potentially changes the action of git fetch jason sprint-36
: if refs/heads/sprint-36
no longer maps to a remote-tracking name, no remote-tracking name will be created or updated.
Whether and when all of this is any use whatsoever is hard to say. (The design is really oriented around --single-branch
clones, rather than fancy user-managed configurations.) Still, this is the underlying mechanism: refspecs pass through refmaps (which I won't attempt to define here, but they are described, rather poorly, in the git fetch
documentation) which are by default built from the per-remote fetch
settings.
Note that without a leading plus sign +
, a refspec like sprint-36:rhs
is a non-forced update. Such an update is done only if the update results in a fast-forward operation. This, in fact, is useful, because it means you can fast-forward your own non-current branches from your own other branches or remote-tracking names. You can do this through either git fetch
or git push
, using .
as the name of the remote, since .
means this Git repository:
git fetch . refs/remotes/origin/master:refs/heads/master
or:
git push . refs/remotes/origin/master:refs/heads/master
will fast-forward your own master
to match your origin/master
, without you having to leave your current non-master
branch. (It won't work if you are on your own master
. There is a tricky way to make it work, but don't do it!)
(You rarely have to spell out the full branch names here, but it's often a good idea just in case—it's annoying when Git mis-matches a reference.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With