21

I've read and tried lots of Git command recommendations and discussion, going on over several days now. It appears that there really is no simple, comprehensive way to make a remote Git repo completely empty -- no branches, no refs, no objects, no files, no nothing.

Yes, I recognize that one could delete and recreate the repo -- if one had that kind of permissions on the origin (which I don't), but that is not the point. How is it done? What combination of Git commands will actually do this, leaving the repo in a virgin state ready to receive whatever we wish to push into it, and with essentially no size (or the minimal size of a virgin repo)?

Please don't tell me this shouldn't be done, or that we have to inform all users, etc. I know all that. I just want to start completely fresh.

Stabledog
  • 3,110
  • 2
  • 32
  • 43
  • Can you just delete and recreate it? – Thomas Aug 07 '13 at 20:17
  • Could use some more context. Why couldn't you just force push your changes to the remote repo? And why do you even want to do this? Also, what exactly have you tried already? Why don't you have permissions to manage the remote repo? Where is the remote repo hosted? Details please! –  Aug 07 '13 at 20:20
  • 1
    http://stackoverflow.com/questions/5363857/clean-out-remote-git-repo-delete-all-files-but-not-the-repo-itself – Alex Aug 07 '13 at 20:20
  • As I indicated in my question, I *do not have permissions* to delete and recreate the repo. The politics is too complex to get into. I can, however, do a push --force, what I've not been able to do successfully is get that to work. I've tried (for example) creating a new repo, setting up the origin, pushing / forcing the repo to the origin, but when done the history is still there and the origin is still 20 mb when cloned. I've been through so many different filter-branch and reset and whatever iterations that I've long since lost count. – Stabledog Aug 07 '13 at 20:24
  • 1
    @Stabledog Can you delete branches from the remote repo? What command are you using to clone it? What ***exact commands*** are you using to force push. Again, details please! –  Aug 07 '13 at 20:26
  • Yes, I can delete branches, I can reset the master to first-commit and push that. But there are apparently still dangling references which keep the objects around, so the size of the repo doesn't shrink, the history doesn't disappear, etc. @Alex: I've tried that approach, it didn't work. I was still able to find tags in the history, and follow them to the trees the represented, etc. I have a feeling that there really isn't anybody who knows how to do this -- who has actually done it, thus the frustrated tone. Sorry. – Stabledog Aug 07 '13 at 20:29
  • 1
    @Stabledog dangling commits can be garbage collected, though if you don't control the remote repo, then garbage collection is probably up to your remote host. Also, you can delete tags. How are you "finding tags" in the history? What commands are you using? Also, when you're cloning from the remote, I doubt that dangling commits are fetched, the only commits that should be fetched are those that are reachable from references/branches. Also, ***are you getting any error messages***? –  Aug 07 '13 at 20:31
  • @Cupcake - your answer did the trick, short and sweet: http://stackoverflow.com/a/18113182/237059. I didn't post a lot of details of everything I've tried, because just composing that list would take a day. But this is the kind of answer I originally thought must exist. – Stabledog Aug 07 '13 at 21:22
  • Hello, by the way, I updated [this answer](http://stackoverflow.com/a/18605496/456814) if you want to check it out, I hope it helps, I tried to make it even clearer and more detailed. –  Jul 09 '14 at 14:53
  • https://gist.github.com/heiswayi/350e2afda8cece810c0f6116dadbe651 – deviant Feb 22 '18 at 08:58

2 Answers2

34

You might want to try pushing an empty local repo with the --mirror flag (emphasis mine):

--mirror

Instead of naming each ref to push, specifies that all refs under refs/ (which includes but is not limited to refs/heads/, refs/remotes/, and refs/tags/) be mirrored to the remote repository. Newly created local refs will be pushed to the remote end, locally updated refs will be force updated on the remote end, and deleted refs will be removed from the remote end. This is the default if the configuration option remote.<remote>.mirror is set.

If your repo is on GitHub, you'll get this error if master is set to the default branch when trying to push:

$ mkdir practice; cd practice;
$ git init; git remote add origin git@github.com:user/practice.git;

$ git push origin --mirror
remote: error: refusing to delete the current branch: refs/heads/master
To git@github.com:user/practice.git
 ! [remote rejected] master (deletion of the current branch prohibited)
error: failed to push some refs to 'git@github.com:user/practice.git'

I got around this by making an initial commit, then pushing.

Obligatory Warning: this will, of course, completely wipe out all of your history and commits in your remote repo—all references, all branches, all tags, etc. Make sure this is actually what you want to do. Of course, you can always make a backup clone of your remote repo before doing this, in case you want to keep it around for whatever reason.

Also note that none of the commits will actually be deleted right away. They'll just become dangling commits, meaning that they're not reachable from a branch. Eventually they'll get garbage collected by Git repos, but if you have access to your remote repo, you can manually start the garbage collection with git gc.

  • 1
    Awesome... that did the trick, and has the elegant simplicity of the right solution! (I also had to add a simple commit). Thanks! – Stabledog Aug 07 '13 at 21:20
  • A follow-up on this from (http://stackoverflow.com/a/18116141/237059). I experimented locally and discovered that pushing with --mirror doesn't actually shrink the origin repo: the disk usage is the same, but the history is no longer accessible, and when that repo is re-cloned, the clone is tiny. So the effect produces what I need for practical matters... I don't really care that an otherwise-inaccessible origin is consuming more disk space than it should. But that's a surprising outcome nevertheless. – Stabledog Aug 08 '13 at 13:04
  • 1
    @Stabledog whoever manages the remote repo (your repo hosting provider, for example), will more than likely run `git gc` (garbage collect) eventually, which will remove all the dangling commits (commits that aren't reachable from a branch/reference), so the disk usage on the remote will eventually shrink to something like what you have with your local clone. This is something I mentioned in [this comment](http://stackoverflow.com/questions/18112966/how-to-delete-purge-remove-all-history-commits-references-branches-from-a-remote/18113182?noredirect=1#comment26519830_18112966). –  Aug 08 '13 at 15:08
  • 1
    Yeah sorry... my level of frustration on this problem produced a reading disability. I'm in rehab now. – Stabledog Aug 08 '13 at 15:11
  • This seems like an incorrect answer, because after doing this there will be an actual commit in the history. Is there a way to do this with truly no commits? (with no commits I mean no commits remaining server-side, so that a push with a history won't be rejected due to a single mismatching initial commit that has no contents) – E. T. Sep 07 '16 at 18:29
  • Wow - this did the trick and nothing else helped. Resetting etc. was pointless. This solution really quickly removes all tags from remote. – dorsz Jun 27 '17 at 21:47
-1

You can't do this. The best you can do is to remove all refs and hope that the server runs git gc and has settings for prune objects that doesn't have any refs. This depends on the server configuration.

Usually it takes 14 days before objects are removed by git gc. However those object won't be cloned if you try to clone the repository.

You've already got a good answer of how to do a "hack" to remove all refs. It works and your repo will appear to you as it is "fresh". However it isn't.

iveqy
  • 19,951
  • 1
  • 15
  • 20
  • Actually it can be done, and turned out to be much simpler than all of the complicated processes I tried based on hints: all one has to do is create an empty repo, specify the origin, and then do a "git push --mirror". Boom... the remote is a new, empty, tiny repo. – Stabledog Aug 08 '13 at 02:12
  • 1
    As I tried to say, it's not empty, it still has objects in it. However when you clone that repo you only get objects that have a ref to them. So your clone of the repo won't contain any of your old objects, but on the server-side they are still there. – iveqy Aug 08 '13 at 02:15
  • What would that even mean? I've cloned the new server repo, and it is indeed tiny and empty now. Nothing else I tried accomplished that. I've looked every way I can at the server repo, and it seems to have nothing in it. So how can there be something there? The --mirror option seems to behave exactly like a binary copy of the .git tree as far as I can tell -- precisely what I wanted. – Stabledog Aug 08 '13 at 03:41
  • Your clone is tiny and empty your server repo is not, it still contains the objects. A clone gives you only objects that's reachable from a ref. If you examine the server repo with for example `du -sh .git` you see that it's bigger than your cloned repo. --mirror do a sync of you refs, not of your objects. – iveqy Aug 08 '13 at 10:59
  • I don't have the access to run shell commands on the server, but I experimented locally and found out that you're right -- even after doing a push --mirror and 'git gc --aggressive', the origin was 82Mb instead of the lightweight repo I pushed. That's very interesting -- however, in my case it doesn't much matter... it doesn't bother me if the repo consumes extra space on the server, because the net effect for those using it is the same: everyone will see it as much smaller when cloning, and the history of the original is unreachable. Thanks! – Stabledog Aug 08 '13 at 12:57
  • @Stabledog `git gc --aggressive` is probably unnecessary. Also, by default, `git gc` will only get rid of dangling commits that are older than 2 weeks. If you want it to garbage collect more, passing `--prune=now` should get rid of everything, see [`git gc`](https://www.kernel.org/pub/software/scm/git/docs/git-gc.html) and [`gc.pruneexpire`](https://www.kernel.org/pub/software/scm/git/docs/git-config.html). –  Aug 08 '13 at 15:13
  • 1
    Ok. Having been a Subversion expert, certain git concepts seem bizarre... like the notion that one would have reference timeouts that engage with garbage collection. It shakes the whole "source control as concrete-and-steel" aesthetic! – Stabledog Aug 08 '13 at 15:15
  • @Stabledog I come from an SVN background too, and I found it a little discomforting at first too. But I got over it. It's ***commits without references*** that are garbage collected. If they were important enough to keep around, then they would be reachable from a reference/branch. Given how frequently a typical proficient Git user creates dangling commits in the repo via things like `rebase`, hard resets, etc, this is a good thing, otherwise your repo would blow up in size and make operations less efficient. Anything that's important enough to keep should be reachable from a reference/branch. –  Aug 08 '13 at 16:31
  • Yeah, ok. The alien concepts are starting to sink in. Thanks for your help and patience! :) – Stabledog Aug 08 '13 at 17:24