2

I have a standalone Git repository, which I would like to migrate to a new SVN repository. At a minimum, I'd like to migrate the master branch with full history, but ideally I'd like to migrate all branches and tags. I only need to do a one-time migration; I do not need to repeat this or do any two-way synchronizations or anything like that.

Based on what I can find online, the best tool to do this seems to be git-svn. I feel like I'm missing something obvious, that there's some parameter to git svn init or git svn dcommit that I need to pass that will make all of this just work, but I just can't find it.

Here's what I've done to try this so far:

Try 1: git svn dcommit to a blank svn repo.

[dyaw@localhost v1]$ mkdir svngit ; cd svngit
[dyaw@localhost svngit]$ git svn init svn://server/emptyRepo --stdlayout
Initialized empty Git repository in /home/dyaw/stackoverflow/v1/svngit/.git/
[dyaw@localhost svngit]$ git svn fetch
[dyaw@localhost svngit]$ git config receive.denyCurrentBranch ignore
[dyaw@localhost svngit]$ cd ..
[dyaw@localhost v1]$ git clone /mnt/sharedDrive/sharedRepo/ plaingit
Cloning into 'plaingit'...
done.
[dyaw@localhost v1]$ cd plaingit/
[dyaw@localhost plaingit]$ git remote add svntest /home/dyaw/stackoverflow/v1/svngit
[dyaw@localhost plaingit]$ git branch -a | grep remotes/origin | grep -v -- '->' | cut -d/ -f3 | xargs -L1 git checkout
Branch 'Foo' set up to track remote branch 'Foo' from 'origin'.
Switched to a new branch 'Foo'
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
[dyaw@localhost plaingit]$ git push svntest --all
Enumerating objects: 2719, done.
Counting objects: 100% (2719/2719), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2591/2591), done.
Writing objects: 100% (2719/2719), 4.67 MiB | 18.55 MiB/s, done.
Total 2719 (delta 2011), reused 237 (delta 123)
remote: Resolving deltas: 100% (2011/2011), done.
To /home/dyaw/stackoverflow/v1/svngit
 * [new branch]      Foo -> Foo
 * [new branch]      master -> master
[dyaw@localhost plaingit]$ git push svntest --tags
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (8/8), 1.32 KiB | 1.32 MiB/s, done.
Total 8 (delta 0), reused 0 (delta 0)
To /home/dyaw/stackoverflow/v1/svngit
 * [new tag]         Many tags pushed
[dyaw@localhost plaingit]$ cd ../svngit/
[dyaw@localhost svngit]$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    Every single file in the repository

[dyaw@localhost svngit]$ git reset --hard
HEAD is now at 3161afd
[dyaw@localhost svngit]$ git status
On branch master
nothing to commit, working tree clean
[dyaw@localhost svngit]$ git svn dcommit
Unable to determine upstream SVN information from HEAD history.
Perhaps the repository is empty. at /usr/libexec/git-core/git-svn line 872.
  • "Perhaps the repository is empty." Yes, it's definitely empty. That's the entire point, I currently have zero content in SVN and I need to move all the work there.
  • This is with a completely empty SVN repo. Not even the trunk/tags/branches directories exist.

Try #2: git svn dcommit to a repo with the basic structure in place (trunk/tags/branches).

  • Same basic result.
  • The output of git svn fetch in the newly-created svngit working copy was:
    [dyaw@localhost svngit]$ git svn fetch
    r1 = 81aad897c91af332c969ece7655a86f8f670bfec (refs/remotes/origin/trunk)
    Checked out HEAD:
      svn://server/basicRepo/trunk r1
    
  • The first git push svntest --all failed with "Updates were rejected because the remote contains work that you do not have locally. This is usually caused by another repository pushing to the same ref. You may want to first integrate the remote changes (e.g., 'git pull ...') before pushing again.". After doing a git pull svntest master --allow-unrelated-histories and committing that merge, the push succeeded.
  • In the end, git svn dcommit gave the same error about an empty repository.

Try #3: Use GitHub's SVN bridge

  • Git repositories on GitHub can be accessed via SVN.
  • I pushed my Git repo to GitHub, and I was able to access it from SVN. (svn co https://github.com....)
  • Normally, svnrdump would be able to dump an SVN repo given a URL (as opposed to svnadmin dump which needs to be given the repo as files in the local filesystem), but svnrdump returns svnrdump: E200007: The requested report is unknown.
  • I am able to svnrdump dump -r a single revision. Any revision seems to work, but specifying --incremental or a range of revisions (-r 1:2 or the default of everything) causes that error to be reported.
  • Apparently GitHub's implementation of SVN over HTTPS doesn't include support for incremental dumps.
  • I could do non-incremental dumps of 1 through HEAD, but I don't know of any way to turn those sequential non-incremental dumps back into an incremental so that it can have the full history.

Try #4: Do an initial commit in SVN then dcommit from there

Per Jeff Mercado's answer.

I created an empty file in the trunk of SVN before doing all the git svn stuff. My first time through, the dcommit had the link to SVN, but didn't actually commit anything on the initial branch, then gave the error when switching to the other branch and when switching back to master. When I repeated those steps to capture the output for posting here, it gave the error right away. Looking back at the command history, I don't see a difference. Regardless, I still can't dcommit.

[dyaw@localhost v4]$ svn co svn://server/emptyRepo svn
Checked out revision 0.
[dyaw@localhost v4]$ cd svn
[dyaw@localhost svn]$ mkdir trunk tags branches
[dyaw@localhost svn]$ svn add trunk tags branches
A         trunk
A         tags
A         branches
[dyaw@localhost svn]$ cd trunk/
[dyaw@localhost trunk]$ touch dummyMigrationFile
[dyaw@localhost trunk]$ svn add dummyMigrationFile
A         dummyMigrationFile
[dyaw@localhost trunk]$ svn ci -m "Added directory structure and dummy file"
Adding         .
Adding         dummyMigrationFile
Transmitting file data .done
Committing transaction...
Committed revision 1.
[dyaw@localhost trunk]$ cd ../..
[dyaw@localhost v4]$ mkdir svngit ; cd svngit
[dyaw@localhost svngit]$ git svn init svn://server/emptyRepo --stdlayout
Initialized empty Git repository in /home/dyaw/stackoverflow/v4/svngit/.git/
[dyaw@localhost svngit]$ git svn fetch
        A       dummyMigrationFile
r1 = a934fa2864bd46f0090c2cdf486c924ceb09fb52 (refs/remotes/origin/trunk)
Checked out HEAD:
  svn://svn1/Data/00000999-migrate/trunk r1
[dyaw@localhost svngit]$ git config receive.denyCurrentBranch ignore
[dyaw@localhost svngit]$ cd ..
[dyaw@localhost v4]$ git clone /mnt/sharedDrive/sharedRepo/ plaingit
Cloning into 'plaingit'...
done.
[dyaw@localhost v4]$ cd plaingit/
[dyaw@localhost plaingit]$ git remote add svntest /home/dyaw/stackoverflow/v4/svngit
[dyaw@localhost plaingit]$ git branch -a | grep remotes/origin | grep -v -- '->' | cut -d/ -f3 | xargs -L1 git checkout
Branch 'Foo' set up to track remote branch 'Foo' from 'origin'.
Switched to a new branch 'Foo'
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
[dyaw@localhost plaingit]$ git push svntest --all
Enumerating objects: 2719, done.
Counting objects: 100% (2719/2719), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2591/2591), done.
Writing objects: 100% (2719/2719), 4.67 MiB | 18.62 MiB/s, done.
Total 2719 (delta 2011), reused 237 (delta 123)
remote: Resolving deltas: 100% (2011/2011), done.
To /home/dyaw/stackoverflow/v4/svngit
 * [new branch]      Foo -> Foo
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to '/home/dyaw/stackoverflow/v4/svngit'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
[dyaw@localhost plaingit]$ git pull svntest master --allow-unrelated-histories --no-edit
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/dyaw/stackoverflow/v4/svngit
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> svntest/master
Merge made by the 'recursive' strategy.
 dummyMigrationFile | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 dummyMigrationFile
[dyaw@localhost plaingit]$ git push svntest --all
Enumerating objects: 593, done.
Counting objects: 100% (593/593), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (552/552), done.
Writing objects: 100% (552/552), 4.05 MiB | 19.00 MiB/s, done.
Total 552 (delta 417), reused 0 (delta 0)
remote: Resolving deltas: 100% (417/417), completed with 30 local objects.
To /home/dyaw/stackoverflow/v4/svngit
   a934fa2..127ed95  master -> master
[dyaw@localhost plaingit]$ git push svntest --tags
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (8/8), 1.32 KiB | 1.32 MiB/s, done.
Total 8 (delta 0), reused 0 (delta 0)
To /home/dyaw/stackoverflow/v4/svngit
 * [new tag]         Lots of tags
[dyaw@localhost plaingit]$ cd ../svngit/
[dyaw@localhost svngit]$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    All the files that were in the original git

[dyaw@localhost svngit]$ git reset --hard
HEAD is now at 127ed95 Merge branch 'master' of /home/dyaw/stackoverflow/v4/svngit
[dyaw@localhost svngit]$ git status
On branch master
nothing to commit, working tree clean
[dyaw@localhost svngit]$ git svn dcommit
Unable to determine upstream SVN information from HEAD history.
Perhaps the repository is empty. at /usr/libexec/git-core/git-svn line 872.

[dyaw@localhost svngit]$ ll
total 4128
-rw-r--r--. 1 dyaw domain users       0 Aug 12 12:47 dummyMigrationFile
-rw-r--r--. 1 dyaw domain users     553 Aug 12 12:48 All the files of the original git
[dyaw@localhost svngit]$ git log | head -n 20
commit 127ed959dd48230e100b24878014b290f795d298
Merge: 3161afd a934fa2
Author: David Yaw <dyaw@localhost>
Date:   Wed Aug 12 12:47:49 2020 -0400

    Merge branch 'master' of /home/dyaw/stackoverflow/v4/svngit

commit a934fa2864bd46f0090c2cdf486c924ceb09fb52
Author: (no author) <(no author)@057915ec-d7cd-45ea-bdf3-a57c6415ee48>
Date:   Wed Aug 12 16:13:26 2020 +0000

    Added directory structure and dummy file

    git-svn-id: svn://server/emptyRepo/trunk@1 057915ec-d7cd-45ea-bdf3-a57c6415ee48

commit 3161afdcb9272a6b047725ccbd1f125b973c7d68
Author: someone else
Date:   Mon May 11 10:10:26 2020 -0400

    This is the last commit on master of the original git repo
[dyaw@localhost svngit]$
David Yaw
  • 27,383
  • 4
  • 60
  • 93
  • GitHub's SVN bridge doesn't support the reports by date because SVN assumes that later revisions do not have earlier dates, but that isn't necessarily true of a Git repository. – bk2204 Aug 12 '20 at 02:24
  • GitHub's SVN bridge manages to order the reports by date in order to show the history, plus dumping any single revision works. svnrdump fails when it needs to get an incremental change from one revision to the next; apparently that's what the GitHub SVN bridge doesn't support. – David Yaw Aug 12 '20 at 18:01
  • @DavidYaw could you resolve this question, I met this situation and I have no idea, could you help me? – Victor Lee Feb 17 '22 at 02:36
  • @DavidYaw check from here: https://stackoverflow.com/questions/71126585/how-migrate-gitlabhas-a-git-repository-commits-logs-to-svn-repository/71159348#71159348 I had resolve that. – Victor Lee Feb 17 '22 at 13:53

1 Answers1

0

In order for git-svn to work, it must be first in sync with an existing svn repository, that way it knows what the current revision is and can create new commits off of. If the repository is completely blank and has no initial commit, there's no way it could sync with it.

You should create a blank initial commit in the SVN repo then sync and dcommit from there.


Otherwise, if you insist on syncing with a blank svn repo, you could try updating the refs by hand to make it appear like there is a base svn commit. Then when you do the dcommit, git-svn will see the "fake base commit" and commit your changes as svn commits. The only caveat is that you would have to rebase your repo off of this empty commit. I'm not even sure if the SVN repository will accept the commits.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • I used a plain svn client to create /trunk/dummyFile in the svn repository before doing the `git init svn` command. I needed to do the `pull --allow-unrelated-histories` bit to make the `git push svntest` command work. After the push from plaingit, the svngit directory still only had the dummy file in it, no other files. dcommit said it was committing, but didn't actually do anything. Switching to the other branch made dcommit give the same error as the original. Switching back to the master branch made all the files appear, but still the same error. I'll add this into my question as try #4. – David Yaw Aug 12 '20 at 16:43