Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove fetched remotes on Git?

Tags:

git

github

I have fetched a few remote branches e.g. git fetch jason sprint-36. I have attached a screenshot (reds are remotes):

enter image description here

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?

like image 304
Kex Avatar asked Dec 28 '18 04:12

Kex


People also ask

How do I remove a remote from git?

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.


2 Answers

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>
like image 149
Meysam Avatar answered Sep 28 '22 16:09

Meysam


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).

Useful background knowledge (none of this is required reading, you can stop here)

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).


Fetch with --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.)

Refspecs, or, more about the git fetches you're running

In 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.)

like image 29
torek Avatar answered Sep 28 '22 16:09

torek