InsOp InsOp - 28 days ago 5
Git Question

git error: There are still logs under

I am trying to use

git fetch --all
but it gives me errors when fetching a certain repository.

error: There are still logs under '.git/logs/refs/remotes/tpickel/RS'


! [new branch] RS -> tpickel/RS (unable to update local ref)

What went wrong and how to fix it?

git error


Apparently there used to be a tpickel/RS/foo (for some name foo).

Your Git recorded information about that, in .git/logs/refs/remotes/tpickel/RS/foo. Now your Git is trying to create a file named .git/logs/refs/remotes/tpickel/RS, which will record information about the new branch tpickel/RS. It cannot do so because there is a directory, .git/logs/refs/remotes/tpickel/RS, in the way. The in-the-way directory contains some file (foo, or some other name, there's nothing above to show what the actual name is), which is how your Git was keeping a reflog for tpickel/RS/foo.

Now that remote branch tpickel/RS is gone, your Git needs to remove both refs/remotes/tpickel/RS/foo and its reflog file. Ideally, git fetch --prune tpickel will do both of those all at once for you, before trying to also create tpickel/RS and its reflog, which is what is failing because of the lingering tpickel/RS/foo.

If the --prune works, you're done. If not, you will have to go in to .git/logs/refs/remotes/tpickel/ and either remove all of the RS and RS/* entries, or rename them to get them out of the way. (If you want to save these, despite the remote tpickel having dropped the branch, move them rather than deleting them, and don't let git fetch --prune delete them. You probably do not want to save them though.)

What --prune means

When you run git fetch remote (such as git fetch tpickel), Git contacts the named remote and gets from it a list of all its references. You can view this list yourself at any time by running git ls-remote remote: this invokes the same start but then stops after getting the list, rather than continuing on to do the fetch.

Suppose that (as is the normal default case) your Git is instructed to copy all their branch references to remote-tracking branches. In this particular example, that would mean copying refs/heads/master to refs/remotes/tpickel/master, for instance. This kind of copying is generally additive: if remote tpickel has branches master and develop today, and tomorrow they add feature, you will have two remote-tracking branches today and you will add a new one tomorrow when you fetch their feature. At some point, though, they may remove a branch. What if they remove feature when you are still using refs/remotes/tpickel/feature?

Git's answer to this puzzle is to default to keeping any copied reference forever. As you have just seen, this can sometimes gets in the way of new branches after old branches have been removed, so this solution is not at all perfect. What adding --prune does is to take the complete list, compare it to your set of remote-tracking branches, and delete from your local repository any remote-tracking branch that has no corresponding branch on the remote. That is, just because we had copied their refs/heads/develop to our refs/remotes/tpickel/develop yesterday, does not mean we should keep refs/remotes/tpickel/develop today, if their refs/heads/develop is gone. So if you specify --prune, Git looks for such occurrences and deletes any stale remote-tracking branches.

This code has been around for a while (since 1.7 or so I think). Git version 1.8.5 added two configuration items: fetch.prune and remote.remote.prune (e.g., remote.tpickel.prune). If fetch.prune is set to true, git fetch always prunes; if the specific remote's configuration is set to true, git fetch prunes that specific remote.

(The code was improved a bit in version 2.0.0 of Git to handle potential ambiguous mappings. This covers cases where multiple upstream references can potentially map to the same remote-tracking reference. With normal setups, this never happens: it can only occur if you make up your own fetch entries for some named remote.)