3

Lets say I've created a new repo (in Github or Gitlab doesnt matter), then:

git clone <uri>
touch test
git add .
git commit -m "Initial"
git push

Which commands do I need to execute such that the remote repo is reseted/reverted to an empty repository without any history, aka, in a state just like when I first created the repo?

Every google search has a git add and git commit in the list of commands to execute, which won't work, since I want a completely clean repo.

Simão Martins
  • 1,210
  • 11
  • 22
  • 1
    Does this answer your question? [Git: How to reset a remote Git repository to remove all commits?](https://stackoverflow.com/questions/2006172/git-how-to-reset-a-remote-git-repository-to-remove-all-commits) – Asif Kamran Malick Apr 07 '21 at 18:03
  • No, that way the repo would have a single commit. – Simão Martins Apr 07 '21 at 18:17
  • If you do as [one of the comments](https://stackoverflow.com/questions/2006172/git-how-to-reset-a-remote-git-repository-to-remove-all-commits#comment1926396_2006252) suggests, you should be good and have a fresh repo in the end just as you desire. It has worked for me in the past, should work for you too. – Asif Kamran Malick Apr 07 '21 at 18:41
  • Recreating the repo in Github/Gitlab directly would work. But I'm trying to automate the process, and having to deal the the Rest API and authentication complicates things a lot. – Simão Martins Apr 07 '21 at 19:09
  • Interesting, but I don't believe that is at all possible. Because at least GitHub has the concept of default branch. At any point of time there will be one default branch and resetting the repo/deleting all commits would mean deleting the branches too. You can delete the non-default branches with `git push :`, but you will never be allowed to delete the default `branch in this manner`. However, the there are rest apis for [deletion and creation of repos](https://docs.github.com/en/rest/reference/repos). Let's hope someone chimes in with a better solution. – Asif Kamran Malick Apr 07 '21 at 19:42
  • I'm curious. Why do you want to do this? Why not just create a new repository? – CryptoFool Apr 08 '21 at 02:32
  • Making tests for a chef recipe that bootstraps a repo by committing some files. However I only want to commit the files if the repo does not have commits. In the end I want to clean the repo so I can run the tests again. – Simão Martins Apr 08 '21 at 14:52

1 Answers1

2

A totally-empty repository, as freshly created by git init, has no commits and no branches (and no tags, and so on). Oddly enough, though, you're on your initial branch—whatever it will be—at this point, even though it does not exist.

Locally, you can get back to this state—well, almost; see footnote 1—simply by deleting every branch. This requires one special trick, because you are never allowed to delete the current branch, whatever it is. So you must use git checkout --orphan or git switch --orphan to set things up so that the current branch is a branch that does not exist. This puts your Git back in that odd state of being on a branch that does not exist. That makes it possible to delete all the other branches.1 You can now change the name of the branch you are on, that does not exist, to some other name—the name you want to come into existence in the future, when you make the initial commit—using git checkout -b or git switch -c.

Hence, the recipe for doing this, assuming you have no tags, no stashes, and so on, is:

git checkout --orphan fakebranch
for name in <insert your list of branch names>; do git branch -D $name; done
git checkout -b main    # or whatever name you want here

Note that the for name in ...; do <commands>; done loop is a shell (sh/bash) construct, not a Git command; in other command line interpreters you may need other commands, or you can just run one git branch -D per branch.

There's one big problem here: you did not ask how to do this locally but rather how to do it on GitHub, GitLab, Bitbucket, and so on. Since those sites do not give you command-line Git, there's no obvious way to make them run git checkout --orphan or something equivalent. In fact, there's no way at all to do that on GitHub, as far as I know. Whether there is on any other hosting site, I don't know. This means that, as Asif Kamran Malick said, you literally can't do it.


1The commits that were find-able through those other branches are not necessarily gone yet. With no way to find them, though, Git will eventually delete those commits. Until that happens, the apparently-empty Git repository will consume extra disk space to hold those not-yet-garbage-collected commits. You can use git gc, or some of its underlying commands, to garbage collect those commits sooner, but there's rarely any reason to bother: just let Git do it on its own, when Git gets around to it.


Close enough?

You can get very close, as noted in Git: How to reset a remote Git repository to remove all commits?, by creating one new root commit, pushing that to the main branch with --force, and deleting all the other branches. Note also that when creating a new repository on GitHub, you can choose to create a new repository with one initial commit. If you make your new root commit here match the GitHub-created root commit, this gets you something that looks like you had GitHub create it, even though you didn't. And, since you suggest creating an initial commit and sending it, your own initial commit will suffice for this purpose.

Since your own recipe listed these commands:

git clone <uri>
touch test
git add .
git commit -m "Initial"
git push

we can make our new root commit consist of a commit with log message Initial and just one empty file named test. To do that, we have the following two recipes, one for Git versions predating 2.23 and one for 2.23 or later:

# git < 2.23
git branch -m main out-of-the-way    # replace main with master if necessary
git checkout --orphan main           # likewise
git rm -rf .
cp /dev/null test                    # adjust per OS
git add test
git commit -m Initial
git push -f origin
git push --delete origin <list of other names> # if needed
for name in <list of names>; do git branch -D $name; done # if needed

or:

# git 2.23 or later
git branch -m main out-of-the-way    # replace main with master if necessary
git switch --orphan main             # likewise
cp /dev/null test                    # adjust per OS
git add test
git commit -m Initial
git push -f origin
git push --delete origin <list of other names> # if needed
for name in <list of names>; do git branch -D $name; done # if needed

Note that the only difference here is that we need not empty out the index (the git rm -rf . step, which also cleans up the working tree).2 This does have to be run from the top level of the working tree in either case (so that the file "test" can be created in the right place).


2This is because git switch --orphan does this for us. The old git checkout --orphan continues not to do it.

torek
  • 448,244
  • 59
  • 642
  • 775
  • So its impossible. Thanks for the detailed answer. – Simão Martins Apr 08 '21 at 14:56
  • @SimãoMartins: well, impossible unless whichever hosting site it is, offers some extra API. But if you're going to the extent of using some web API you might as well use the one that deletes the repository, then use the one that makes a new empty repository. :-) – torek Apr 08 '21 at 23:48