4

I have a Git repository with dozens of commits. I want the last commit to be the only commit - meaning the repository would behave as if it was just initialized and the current file state is the only commit.

Is there a better way to do it than creating a new repo?

random
  • 9,774
  • 10
  • 66
  • 83
Guy
  • 12,488
  • 16
  • 79
  • 119
  • There is no way to do this that's not functionally equivalent to creating a new repo. `rm -rf .git; git init .` seems simpler, although you lose configuration. – millimoose Jul 27 '13 at 23:01
  • @millimoose I guess you're right, it is simpler just to do it that way. If you put it as an answer, I'll upvote it. –  Jul 27 '13 at 23:09
  • Duplicate of [Git squash all commits into a single commit](http://stackoverflow.com/questions/1657017/git-squash-all-commits-into-a-single-commit) –  Jun 11 '14 at 03:25

2 Answers2

11

Use the following:

$ git checkout --orphan new-master master
$ git commit -m "Initial commit for new root"

# Compare new-master with master to double-check that
# their final commit states are identical:
$ git diff new-master master

# If there is no diff/patch output, then they're identical,
# so it's safe to delete master:
$ git branch -D master
$ git branch --move new-master master

The --orphan flag creates an entirely separate commit tree, with a different root commit. It follows basic branching syntax, so the arguments that follow are the name for the new branch, followed by the starting point, which can be any Git revision specification, including a commit sha or a branch name.

So in the first command, you tell Git to make a new orphan branch starting with the same state as the current state of the master branch. Then you need to commit the new root, just as you would have done the first time with your original root.

Next you want to make sure that the final state of new-master really is identical to the final commit state of master, so do a diff of them to make sure (no diff output means they are identical). Then to cleanup, you delete the old master branch and rename the new branch to it.

Here's the explanation of the --orphan flag from the official Linux Kernel Git documenation for git-checkout(1) (with missing syntax correction by me on the first line):

--orphan <new_branch> [<start_point>]

Create a new orphan branch, named <new_branch>, started from <start_point> and switch to it. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from all the other branches and commits.

The index and the working tree are adjusted as if you had previously run git checkout <start_point>. This allows you to start a new history that records a set of paths similar to <start_point> by easily running git commit -a to make the root commit.

This can be useful when you want to publish the tree from a commit without exposing its full history. You might want to do this to publish an open source branch of a project whose current tree is "clean", but whose full history contains proprietary or otherwise encumbered bits of code.

If you want to start a disconnected history that records a set of paths that is totally different from the one of <start_point>, then you should clear the index and the working tree right after creating the orphan branch by running "git rm -rf ." from the top level of the working tree. Afterwards you will be ready to prepare your new files, repopulating the working tree, by copying them from elsewhere, extracting a tarball, etc.

  • Everything went ok until I tried to push back to github and I got a "hint: Updates were rejected because the tip of your current branch is behind" – Guy Jul 27 '13 at 22:52
  • ok - read a bit more and tried "git push origin :master" - almost made it (it's prohibited to delete on github...) – Guy Jul 27 '13 at 22:59
  • @Guy You usually need to force pushes that rewrite history. The reason behind this is that it's a fantastically bad idea if anyone else checked out your repository ever. What you're doing is really completely indistinguishgable from deleting the GitHub repo and starting a new one as far as any other linked repo is concerned. – millimoose Jul 27 '13 at 22:59
  • @Guy millimoose is correct, you'd need to force push. Though of course this is not recommended to do if you're sharing the repo with other users, or without at least coordinating the change with them first. But if it's a private repo, then it's fine. Force push by doing `git push origin master -f`. Also, deleting branches is not prohibited on GitHub by default, unless you don't have write permissions for the remote repo, or you ask GitHub support to disable deletions...why are you having trouble deleting? –  Jul 27 '13 at 23:04
  • hey, it worked! tnx. one commit in github. that's great. had all my API passwords in a public repo - now i'm good :-). – Guy Jul 27 '13 at 23:17
7

If you don't need to preserve the repo configuration, it's easiest to just do

rm -rf .git/
git init .

Even then I think just backing up ~/.git/config and ~/.git/info/exclude should be sufficient.

millimoose
  • 39,073
  • 9
  • 82
  • 134