views:

1143

answers:

2

Here is my problem:

  1. I used Subversion for some time, until I switched to Git. Some more time elapsed.
  2. There was no import of the history from Subversion to Git. It was a strict checkout, delete of the .svn dirs, then git init. Not a smart move.
  3. Now, thousands of git commits later, I find a backup of the Subversion repo made at the time the first git commit occurred. Aha!

I would like to roll back the git repo to day 0, properly import the svn repo, then reapply all git changes, thus correcting what was not done the first time.

Has anyone attempted this? How would I go about doing this? It sounds like the mother of all rebases.

+1  A: 

I've never tried what you want, but I'm doing something similar with CVS.

Basically, I'd suggest:

  1. Create a new git repository with the history from svn
  2. In this new repository, git fetch everything from the git repository (there'll be no commits in common)
  3. Then, git branch remote/branch branch-last and git rebase --onto svn-last remote/branch-first branch-last, where remote/branch-first is the first commit of your imported git repository, etc.

If you have more branches, things are more complicated. I think repeating step 3 might do, but you'd better better to try yourself. If you have merges in your git history, you might need git rebase -i -p .... Remember, the advantage of git is you basically can't screw up anything (esp. if you're working in a separate repository).

jpalecek
+10  A: 

Sounds like a job for git grafts. The documentation for this is a bit sketchy, but basically what you want to do is this:

  1. Get a git-svn copy of of the svn in your repo. Easiest way to do this is to git svn clone in, then fetch the the svn clone in your existing repository
  2. Figure out which base commit should follow the svn commit. So you probably have a git root somewhere ("Last SVN version") which should follow the last actual SVN version. This is the head of git-svn clone you just fetched
  3. Create a file .git/info/grafts and put the two sha's on a single line there. The first is the first git commit, then a space, then the last svn commit. This tells git that the git commit is not parentless, but has in fact the last svn commit as parent.
  4. You can now check with gitk/gitx/whatever that the two repositories are connected
  5. To make the change permanent, run git filter-branch. You'll probably want to read its manpage first.

You can do step 3 for all your branches too of course. Problem with jpalecek's approach is that the rebase will flatten your history, so if you had any merges, the rebase will lose them. The filter-branch approach keeps your history intact.

Pieter
This is exactly what I did for memcached: http://groups.google.com/group/memcached/browse_thread/thread/a7b153bc087244b1/4023dbe7168ea5fa
Dustin
Nice write-up, Pieter. You have made git grafts simple. +1
Paul
Thanks Pieter, +1 from me. The same process with a couple of images can also be found at http://blog.experimentalworks.net/2009/07/git-voodoo/
Mark van Lent
Couple of points - run git filter-branch with --tag-name-filter cat to convert tags, and also, don't forget to comment out/delete the .git/info/grafts file when you're done, as otherwise it doesn't seem to be possible to push to a remote repository to update it.
Simon Howard