Recent changes in policy re-ignited our quest to migrate away from Subversion to GIT, as GIT is much more powerful for branching and merging e.t.c.
Migrating from Subversion is not very hard; you just start over, right? So you want to lose all the project history? I don’t think so.
The migrated GIT-repository should include all history, all commit messages, all tags, all branches. How do you do that?
David Zych wrote a great article describing how to migrate from SVN to GIT.
(by the way, David; if you ever read this; you have a typo in your article; the switch for branches is not -B
but -b
-- lowercase as opposed to uppercase.)
As it turns out; GIT has tools for that! But the steps in the article did not work for us;
After running the command, I got a folder with trunk, branches, tags e.t.c. instead if the actual project. Turns out, this is OUR mistake.
When we set-up our Subversion server, we did not want to go through the hassle of creating a repository for every single plugin, theme, tool we created. So we just created 3 repositories; plugins
, themes
, other
.
The tools as described by David do not take this into account. These are the changes to Davids article we had to make;
Because our project is one directory deep in a “global” repository, as opposed to the root of that repository, we do not point to the project-url, but to the actual repository the project is in;
Let’s see the git init command:
git svn init http://some.svn.server/my-project --prefix svn \
-T Trunk -b Branches -t Tags
or
git svn init <URL to repository> --prefix svn \
-T <name of trunk> -b <name of branches> -t <name of tags>
Example of a normal SVN repo:
svn://some.svn.server/my-project
Example of one of our repositories:
svn://some.svn.server/plugins/my-project
Which is in fact a sub-directory of the actual repository
svn://some.svn.server/plugins
and my-project is the name of that sub-directory.
One would think you need to use
svn://some.svn.server/plugins/my-project
svn://some.svn.server/plugins
So;
git svn init svn://some.svn.server/plugins --prefix svn \
-T <name of trunk> -b <name of branches> -t <name of tags>
As we surely do not want to migrate EVERYTHING we have in our plugins repository into our new project-level GIT repository, we specify the project by adding it as the tags/branches/trunk directory;
git svn init svn://some.svn.server/plugins --prefix svn \
-T my-project/trunk -b my-project/branches -t my-project/tags
Because our folders don’t start with a /, GIT will complain we need a / at the end of our prefix, so --prefix svn
becomes --prefix svn/
Now the complete command reads:
git svn init svn://some.svn.server/plugins --prefix svn/ \
-T my-project/trunk -b my-project/branches -t my-project/tags
And that’s it; now we continue with the rest of Davids article :)
Now a hint; to link all the branches, the article states to
git branch -a
git branch <new branch name> remotes/svn/<current branch name>
git branch -a
This command lists tags too, so it might be handy to filter that list;
git branch -a | egrep -v 'tags|trunk|master'
And now we can automate the branch-linking;
for branch in $(git branch -a | egrep -v 'tags|trunk|master'); do \
branchname=$(echo $branch | sed 's_remotes/svn/__g'); \
git branch $branchname $branch ; done
And for the tags, we do the same, or, almost the same ;)
for tag in $(git branch -a | grep 'remotes/svn/tags/'); do \
tagname=$(echo $tag | sed 's_remotes/svn/tags/__g'); \
git tag -a -m "Migrating SVN tag $tagname" $tagname $tag ; done
Now, a note here; for some reason, David writes in his article; refs/tags/tag-name
but that gives me an error. This looks to be a typo as well as I think it should read:
refs/remotes/svn/tags/tag-name
A final note, on what I think the beauty of GIT is; it is the distributed-ness of the repositories; all we did up til this point is all local. Nothing is pushed to GitHub or Bitbucket or GitLab, or whatever service you prefer to use. It is all just on your computer. If you messed up; delete the directory, and start again. To check if you did things right, I suggest using a powerful git GUI application, like SourceTree or GitHub Desktop.
When you are ready to make things permanent, we continue to the final command sequence;
git remote add newrepo https://url.to.git/repo.git
git push --all newrepo
git push --tags newrepo
newrepo
is a name for the remote, it’s not something you will be dealing with after this. And of course; https://url.to.git/repo.git
should be an existing, empty repository on [insert your favourite GIT service here] :)
Good luck, and happy migrating!
Addendum: the cliff-notes version:
- create an empty repository on (for example) GitHub.com, copy the git-repository-url, for example:
git@github.com:username/my-project.git
- create a directory on your computer and go there (MacOS, Linux or WSL on windows);
mkdir ~/Desktop/svn-to-git-migration && \
cd ~/Desktop/svn-to-git-migration - Initialize the git-svn repo;
git svn init svn://some.svn.server/plugins --prefix svn/ \
-T my-project/trunk -b my-project/branches -t my-project/tags - Point git to your author-map (see Davids article for details);
git config svn.authorsfile /path/to/authors.txt
- Fetch the SVN data;
git svn fetch
- Get a cuppa while you wait
- Migrate branches;
for branch in $(git branch -a | egrep -v 'tags|trunk|master'); do \
branchname=$(echo $branch | sed 's_remotes/svn/__g'); \
git branch $branchname $branch ; done - Migrate tags;
for tag in $(git branch -a | grep 'remotes/svn/tags/'); do \
tagname=$(echo $tag | sed 's_remotes/svn/tags/__g'); \
git tag -a -m "Migrating SVN tag $tagname" $tagname refs/$tag ; done - Link your local git repo to the upstream repo;
git remote add newrepo <insert repo-url from 1 here>
- Push data and branches;
git push --all newrepo
- Push tags;
git push --tags newrepo
- Checkout your remote in a fresh directory to work with (or check your migration);
git clone <insert repo-url from 1 here> ~/Desktop/fresh-checkout